animated_tree_view 1.1.0
animated_tree_view: ^1.1.0 copied to clipboard
Animated TreeView based on AnimatedList that allows building fully customizable Nodes that can be nested to infinite levels and children.
animated_tree_view #
A flutter package that provides a heirarchial Tree like data structure that can be visualized as a linear list view.
The widget is based on the Flutter’s AnimatedList widget and can even be used as a replacement to the AnimatedList. The widget data is completely customizable and provides an LeveledItemWidgetBuilder
to build the tree items.
Variants #
There are two variants available in the package: the simple TreeView
and the more comprehensive TreeView.indexed
. A sliver variant is also available as SliverTreeView
.
TreeView.simple #
The TreeView
uses a Map data-structure to handle the Nodes and their children, using a Map makes the TreeView
more performant with a complexity on traversing the Nodes being O(n), where n is the level of the node. However the simple TreeView
lacks the indexed based operations like insertAt
and removeAt
operations.
TreeView.indexed #
The TreeView.indexed
uses a List data-structure to handle the Nodes and their children. This allows it to perform all the list based operations that require indices, like insertAt
or removeAt
. The drawback of using an TreeView.indexed
instead of TreeView
is that the Node traversal operations on the TreeView.indexed
are more expensive with a complexity of O(n^m), where n is the number of children in a node, and m is the node level.
SliverTreeView #
For implementing fancy lists and animations using slivers, SliverTreeView
should be used. It is based on the SliverAnimatedList. It provides the same APIs as the normal TreeView
and its usage is also the same. Slivers also provide significant performance improvements over the regular lists, to get to know more about slivers see here.
Features #
- Infinite levels and child nodes.
- Animations for Node expansion and collapse.
- Familiar API due to inspiration from AnimatedList.
- Provides plenty of utility methods for adding, inserting and removing child nodes.
- Easily traverse the tree laterally or vertically from the root to the leaf and back.
- Tree Diff Util to compute the difference between two trees, and automatically apply the changes in the tree view.
- Implementation of ValueListenable makes it easy to listen to changes in the data.
How to use #
TreeView #
You can simply use the provided TreeNode
or extend your data object from TreeNode<T>
like this
To use your own custom data with [TreeView], wrap your model [T] in [TreeNode] like this:
class YourCustomNode extends TreeNode<CustomClass> {
...
}
Note: If the key
is omitted, then a unique key will be automatically assigned to the Node.
Finally, initialize the TreeView
by providing it a builder.
TreeView.simple(
tree: TreeNode.root(),
builder: (context, level, node) {
// build your node item here
// return any widget that you need
return ListTile(
title: Text("Item ${node.level}-${node.key}"),
subtitle: Text('Level $level'),
);
}
TreeView.indexed #
The usage of TreeView.indexed
is exactly the same as a simple TreeView
. You only need to replace TreeNode
with IndexedTreeNode
or extend your YourCustomNode
from IndexedTreeNode
like this
class YourCustomNode extends IndexedTreeNode<CustomClass> {
...
}
Finally initialize the widget like this:
TreeView.indexed(
tree: IndexedTreeNode.root(),
builder: (context, level, node) {
// build your node item here
// return any widget that you need
return ListTile(
title: Text("Item ${node.level}-${node.key}"),
subtitle: Text('Level $level'),
);
}
Please see this example for a more comprehsive code sample.
SliverTreeView #
The API and usage of SliverTreeView
is the same as TreeView
. You just need to wrap the SliverTreeView
inside a CustomScrollView
and provide it a Tree
and a builder.
CustomScrollView(
slivers: [
SliverTreeView.simple(
tree: TreeNode.root(),
builder: (context, level, node) {
// build your node item here
// return any widget that you need
return ListTile(
title: Text("Item ${node.level}-${node.key}"),
subtitle: Text('Level $level'),
);
},
),
],
);
Configuration and Behavior #
Attributes | Description |
---|---|
builder | The builder function that is provided to the item builder. Called, as needed, to build list item widgets. The built widget is passed to the AnimatedList's itemBuilder. |
tree | Tree that is used to populate the TreeView . If the tree is updated using any state management tools like setState or Bloc, then the TreeDiffUtil is used to get the diff between the two trees, and apply all the changes from the new tree onto the old tree. |
scrollController | Provide a scrollController for more granular control over scrolling behavior. |
expansionIndicator | Provide an ExpansionIndicator to set the expand widget and collapse widget. Typically these are Icon widgets. You can pass in null if you do not want to show any expansion indicator. |
indentPadding | This is the padding is applied to the start of an item. IndentPadding will be multiplied by Node-Level before being applied. |
onItemTap | callback that can be used to handle any action when an item is tapped or clicked. |
showRootNode | Flag to show the Root Node in the TreeView. |
expansionBehavior | The ExpansionBehavior provides control over the behavior of the node when it is expanded. See ExpansionBehavior for available behaviors. |
padding | The amount of space by which to inset the children. |
primary | Whether this is the primary scroll view associated with the parent PrimaryScrollController. |
physics | An object that can be used to control the position to which this scroll view is scrolled. |
shrinkWrap | Whether the extent of the scroll view in the scrollDirection should be determined by the contents being viewed. |
ExpansionBehavior #
The 'ExpansionBehavior' provides control over the behavior of the node when it is expanded. There are five available ExpansionBehaviors to choose from.
**Note: For using an ExpansionBehavior
with a SliverTreeView
, the same instance of an AutoScrollController
needs to be provided to SliverTreeView
and the CustoScrollView
.
ExpansionBehavior.none
#
No additional action will be taken on node expansion.
ExpansionBehavior.scrollToLastChild
#
The list will be scrolled to the last child of the node if it is not already visible on screen. This ensures that the last child is always visible.
ExpansionBehavior.snapToTop
#
The expanded node will be snapped to the top of the list. This ensures that the expanded node is always visible with maximum number of children.
ExpansionBehavior.collapseOthers
#
Collapse all other nodes, only the current node will remain expanded. This ensures that only one node is expanded at one time.
ExpansionBehavior.collapseOthersAndSnapToTop
#
Collapse all other nodes, only the current node will remain expanded, also snap the node to the top of the list. This ensures that only one node is expanded at one time.
Tree Diff Util #
A TreeDiffUtil
is used to determine the difference between two trees if the tree
is udpated using any state management tool like setState or Bloc etc.
For TreeView.simple
, which uses a Map
internally to store the child nodes, this is a simple difference operation on all the nodes of the tree. Complexity is O(2n), where n is the total number of nodes in the tree.
For TreeView.indexed
, which uses a List
internally to store the child nodes, it is a little more complex as Myer's algorithm is used to determine the difference in the children of each respective node. Complexity is O(N + D^2), where D is the length of the edit script. For more details see diffutil_dart.
Available APIs #
Node #
Method | TreeView | IndexedTreeView | Description |
---|---|---|---|
isRoot |
✅ | ✅ | Getter to check if the node is a root |
isLeaf |
✅ | ✅ | Getter to check if the node is a Leaf |
root (getter) |
✅ | ✅ | Getter to get the root node. If the current node is not a root, then the getter will traverse up the path to get the root. |
level |
✅ | ✅ | Getter to get the level i.e. how many iterations it will take to get to the root. |
elementAt |
✅ | ✅ | Utility method to get a child any child node at the path. The path contains the keys of the node separated by period . e.g. |
add |
✅ | ✅ | Add a child to the node |
addAll |
✅ | ✅ | Add a collection of nodes to the node |
remove |
✅ | ✅ | Remove a child from the node |
removeAll |
✅ | ✅ | Remove a collection of nodes from the node |
removeWhere |
✅ | ✅ | Remove children from the node that meet the criterion in the provided test |
delete |
✅ | ✅ | Delete the current node |
clear |
✅ | ✅ | Remove all the child nodes: after this operation the children are empty |
first |
⭕ | ✅ | Get/Set first child in the node |
last |
⭕ | ✅ | Get/Set last child in the node |
insert |
⭕ | ✅ | Insert a child at an index in the node |
insertAll |
⭕ | ✅ | Insert a list of children at an index in the node |
insertAfter |
⭕ | ✅ | Insert a child after the node |
insertBefore |
⭕ | ✅ | Insert a child before the node |
removeAt |
⭕ | ✅ | Remove a child at the index |
firstWhere |
⭕ | ✅ | Get the first child node that matches the criterion in the test. |
lastWhere |
⭕ | ✅ | Get the last child node that matches the criterion in the test. |
indexWhere |
⭕ | ✅ | Get the index of the first child node that matches the criterion in the test. |
TreeViewController #
The TreeViewController
provides utility methods that allow controlling the TreeView
programmatically.
Method | Description |
---|---|
elementAt | Get any item at path from the root. The keys of the items to be traversed should be provided in the path |
root | Root node of the TreeView |
scrollToIndex | Allows to scroll to any item with index in the list. If you do not have the index of the item, then use the alternate scrollToItem method item instead. |
scrollToItem | Utility method to scroll to any visible item in the tree. |
toggleExpansion | Utility method to expand or collapse an item. |