flutter_fancy_tree_view 1.6.0 flutter_fancy_tree_view: ^1.6.0 copied to clipboard
A collection of widgets and slivers that helps bringing hierarchical data to life.
Please, visit the Live Demo App to fiddle around with the features of this package.
import 'package:flutter/material.dart';
import 'package:flutter_fancy_tree_view/flutter_fancy_tree_view.dart';
void main() => runApp(const MaterialApp(home: Scaffold(body: MyTreeView())));
// Create a class to hold your hierarchical data (optional, could be a Map or
// any other data structure that's capable of representing hierarchical data).
class MyNode {
const MyNode({
required this.title,
this.children = const <MyNode>[],
});
final String title;
final List<MyNode> children;
}
class MyTreeView extends StatefulWidget {
const MyTreeView({super.key});
@override
State<MyTreeView> createState() => _MyTreeViewState();
}
class _MyTreeViewState extends State<MyTreeView> {
// In this example a static nested tree is used, but your hierarchical data
// can be composed and stored in many different ways.
static const List<MyNode> roots = <MyNode>[
MyNode(
title: 'Root 1',
children: <MyNode>[
MyNode(
title: 'Node 1.1',
children: <MyNode>[
MyNode(title: 'Node 1.1.1'),
MyNode(title: 'Node 1.1.2'),
],
),
MyNode(title: 'Node 1.2'),
],
),
MyNode(
title: 'Root 2',
children: <MyNode>[
MyNode(
title: 'Node 2.1',
children: <MyNode>[
MyNode(title: 'Node 2.1.1'),
],
),
MyNode(title: 'Node 2.2')
],
),
];
// This controller is responsible for both providing your hierarchical data
// to tree views and also manipulate the states of your tree nodes.
late final TreeController<MyNode> treeController;
@override
void initState() {
super.initState();
treeController = TreeController<MyNode>(
// Provide the root nodes that will be used as a starting point when
// traversing your hierarchical data.
roots: roots,
// Provide a callback for the controller to get the children of a
// given node when traversing your hierarchical data. Avoid doing
// heavy computations in this method, it should behave like a getter.
childrenProvider: (MyNode node) => node.children,
);
}
@override
void dispose() {
// Remember to dispose your tree controller to release resources.
treeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// This package provides some different tree views to customize how
// your hierarchical data is incorporated into your app. In this example,
// a TreeView is used which has no custom behaviors, if you wanted your
// tree nodes to animate in and out when the parent node is expanded
// and collapsed, the AnimatedTreeView could be used instead.
//
// The tree view widgets also have a Sliver variant to make it easy
// to incorporate your hierarchical data in sophisticated scrolling
// experiences.
return TreeView<MyNode>(
// This controller is used by tree views to build a flat representation
// of a tree structure so it can be lazy rendered by a SliverList.
// It is also used to store and manipulate the different states of the
// tree nodes.
treeController: treeController,
// Provide a widget builder callback to map your tree nodes into widgets.
nodeBuilder: (BuildContext context, TreeEntry<MyNode> entry) {
// Provide a widget to display your tree nodes in the tree view.
//
// Can be any widget, just make sure to include a [TreeIndentation]
// within its widget subtree to properly indent your tree nodes.
return MyTreeTile(
// Add a key to your tiles to avoid syncing descendant animations.
key: ValueKey(entry.node),
// Your tree nodes are wrapped in TreeEntry instances when traversing
// the tree, these objects hold important details about its node
// relative to the tree, like: expansion state, level, parent, etc.
//
// TreeEntrys are short lived, each time TreeController.rebuild is
// called, a new TreeEntry is created for each node so its properties
// are always up to date.
entry: entry,
// Add a callback to toggle the expansion state of this node.
onTap: () => treeController.toggleExpansion(entry.node),
);
},
);
}
}
// Create a widget to display the data held by your tree nodes.
class MyTreeTile extends StatelessWidget {
const MyTreeTile({
super.key,
required this.entry,
required this.onTap,
});
final TreeEntry<MyNode> entry;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
// Wrap your content in a TreeIndentation widget which will properly
// indent your nodes (and paint guides, if required).
//
// If you don't want to display indent guides, you could replace this
// TreeIndentation with a Padding widget, providing a padding of
// `EdgeInsetsDirectional.only(start: TreeEntry.level * indentAmount)`
child: TreeIndentation(
entry: entry,
// Provide an indent guide if desired. Indent guides can be used to
// add decorations to the indentation of tree nodes.
// This could also be provided through a DefaultTreeIndentGuide
// inherited widget placed above the tree view.
guide: const IndentGuide.connectingLines(indent: 48),
// The widget to render next to the indentation. TreeIndentation
// respects the text direction of `Directionality.maybeOf(context)`
// and defaults to left-to-right.
child: Padding(
padding: const EdgeInsets.fromLTRB(4, 8, 8, 8),
child: Row(
children: [
// Add a widget to indicate the expansion state of this node.
// See also: ExpandIcon.
FolderButton(
isOpen: entry.hasChildren ? entry.isExpanded : null,
onPressed: entry.hasChildren ? onTap : null,
),
Text(entry.node.title),
],
),
),
),
);
}
}