This tutroial is dedicated at looking at easy to re-use TreeView examples. A widget like a TreeView gives us a great way of representing hierarchical data.
Because this widget is not included natively in Flutter SDK, we will mostly be using third party libraries to create it. Most of such libraries are constructed based on ListView.
Example 1: TreeView using ListTreeView
ListTreeView is a treeview for Flutter based on the listview.
Here are it's features:
- Highly customizable. It only manages the tree structure of the data, and the UI is designed by yourself.
- Performance is efficient because of the Listview's reuse mechanism.
- Infinitely increasing child levels and child nodes
Here is the screenshot demo of the project we will create:
Step 1: Create Project
Start by creating an empty Android Studio
project.
Step 2: Dependencies
To install it, first depend on it in your pubspec.yaml
:
dependencies:
list_treeview: 0.3.0
Then sync or flutter pub get
to fetch it.
Step 2: Use it
Start by importing it:
import 'package:list_treeview/list_treeview.dart';
Then initialize the TreeViewController
:
class _TreePageState extends State<TreePage> {
TreeViewController _controller;
@override
void initState() {
super.initState();
///The controller must be initialized when the treeView create
_controller = TreeViewController();
}
}
Then create a class to represent each Tree node:
/// The data class that is bound to the child node
/// You must inherit from NodeData !!!
/// You can customize any of your properties
class TreeNodeData extends NodeData {
TreeNodeData({this.label,this.color}) : super();
/// Other properties that you want to define
final String label;
final Color color;
String property1;
String property2;
String property3;
///...
}
Now set the data:
void getData() async {
print('start get data');
_isSuccess = false;
await Future.delayed(Duration(seconds: 2));
var colors1 = TreeNodeData(label: 'Colors1');
var color11 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 0, 139, 69));
var color12 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 0, 191, 255));
var color13 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 255, 106, 106));
var color14 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 160, 32, 240));
colors1.addChild(color11);
colors1.addChild(color12);
colors1.addChild(color13);
colors1.addChild(color14);
var colors2 = TreeNodeData(label: 'Colors2');
var color21 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 255, 64, 64));
var color22 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 28, 134, 238));
var color23 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 255, 106, 106));
var color24 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 205, 198, 115));
colors2.addChild(color21);
colors2.addChild(color22);
colors2.addChild(color23);
colors2.addChild(color24);
/// set data
_controller.treeData([colors1, colors2]);
print('set treeData suceess');
setState(() {
_isSuccess = true;
});
}
To Insert a Node:
_controller.insertAtFront(dataNode,newNode);
//_controller.insertAtRear(dataNode, newNode);
//_controller.insertAtIndex(1, dataNode, newNode);
To Remove a node:
_controller.removeItem(item);
To Expand or collapse a node, pass the index:
/// Control item to expand or collapse
/// [index] The index of the selected item
_controller.expandOrCollapse(index);
To Select and single or all child nodes:
/// select only itself
_controller.selectItem(item);
/// Select itself and all child nodes
_controller.selectAllChild(item);
Full Example
Here is a full TreeView example using this library:
main.dart
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:list_treeview/list_treeview.dart';
import 'package:list_treeview/tree/tree_view.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: RaisedButton(
child: Text('TreeView'),
onPressed: () {
Navigator.push(
context, CupertinoPageRoute(builder: (_) => TreePage()));
},
),
),
);
}
}
/// The data class that is bound to the child node
/// You must inherit from NodeData !!!
/// You can customize any of your properties
class TreeNodeData extends NodeData {
TreeNodeData({this.label, this.color}) : super();
/// Other properties that you want to define
final String? label;
final Color? color;
String? property1;
String? property2;
String? property3;
///...
}
class TreePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _TreePageState();
}
}
class _TreePageState extends State<TreePage>
with SingleTickerProviderStateMixin {
TreeViewController? _controller;
bool _isSuccess = false;
List<Color> _colors = [];
@override
void initState() {
super.initState();
///The controller must be initialized when the treeView create
_controller = TreeViewController();
for (int i = 0; i < 100; i++) {
if (randomColor() != null) {
_colors.add(randomColor());
}
}
///Data may be requested asynchronously
getData();
}
void getData() async {
print('start get data');
_isSuccess = false;
await Future.delayed(Duration(seconds: 2));
var colors1 = TreeNodeData(label: 'Colors1');
var color11 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 0, 139, 69));
var color12 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 0, 191, 255));
var color13 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 255, 106, 106));
var color14 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 160, 32, 240));
colors1.addChild(color11);
colors1.addChild(color12);
colors1.addChild(color13);
colors1.addChild(color14);
var colors2 = TreeNodeData(label: 'Colors2');
var color21 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 255, 64, 64));
var color22 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 28, 134, 238));
var color23 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 255, 106, 106));
var color24 = TreeNodeData(
label: 'rgb(0,139,69)', color: Color.fromARGB(255, 205, 198, 115));
colors2.addChild(color21);
colors2.addChild(color22);
colors2.addChild(color23);
colors2.addChild(color24);
/// set data
_controller!.treeData([colors1]);
print('set treeData suceess');
setState(() {
_isSuccess = true;
});
}
@override
void dispose() {
super.dispose();
}
Color getColor(int level) {
return _colors[level % _colors.length];
}
Color randomColor() {
int r = Random.secure().nextInt(200);
int g = Random.secure().nextInt(200);
int b = Random.secure().nextInt(200);
return Color.fromARGB(255, r, g, b);
}
/// Add
void add(TreeNodeData dataNode) {
/// create New node
// DateTime time = DateTime.now();
// int milliseconds = time.millisecondsSinceEpoch ~/ 1000;
int r = Random.secure().nextInt(255);
int g = Random.secure().nextInt(255);
int b = Random.secure().nextInt(255);
var newNode = TreeNodeData(
label: 'rgb($r,$g,$b)', color: Color.fromARGB(255, r, g, b));
_controller!.insertAtFront(dataNode, newNode);
// _controller.insertAtRear(dataNode, newNode);
// _controller.insertAtIndex(1, dataNode, newNode);
}
void delete(dynamic item) {
_controller!.removeItem(item);
}
void select(dynamic item) {
_controller!.selectItem(item);
}
void selectAllChild(dynamic item) {
_controller!.selectAllChild(item);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TreeView'),
),
body: _isSuccess ? getBody() : getProgressView(),
);
}
Widget getProgressView() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget getBody() {
return ListTreeView(
shrinkWrap: false,
padding: EdgeInsets.all(0),
itemBuilder: (BuildContext context, NodeData data) {
TreeNodeData item = data as TreeNodeData;
// double width = MediaQuery.of(context).size.width;
double offsetX = item.level * 16.0;
return Container(
height: 54,
padding: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(width: 1, color: Colors.grey))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.only(left: offsetX),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 5),
child: InkWell(
splashColor: Colors.amberAccent.withOpacity(1),
highlightColor: Colors.red,
onTap: () {
selectAllChild(item);
},
child: data.isSelected
? Icon(
Icons.star,
size: 30,
color: Color(0xFFFF7F50),
)
: Icon(
Icons.star_border,
size: 30,
color: Color(0xFFFFDAB9),
),
),
),
Text(
'level-${item.level}-${item.indexInParent}',
style: TextStyle(
fontSize: 15, color: getColor(item.level)),
),
SizedBox(
width: 10,
),
// Text(
// '${item.label}',
// style: TextStyle(color: item.color),
// ),
],
),
),
),
Visibility(
visible: item.isExpand,
child: InkWell(
onTap: () {
add(item);
},
child: Icon(
Icons.add,
size: 30,
),
),
)
],
),
);
},
onTap: (NodeData data) {
print('index = ${data.index}');
},
onLongPress: (data) {
delete(data);
},
controller: _controller,
);
}
}
Run
Copy the code or download it in the link below, build and run.
Reference
Here are the reference links:
Number | Link |
---|---|
1. | Download Example |
2. | Follow code author |