diff --git a/lib/src/sliver_animated_tree.dart b/lib/src/sliver_animated_tree.dart index a8e74e1..96a6eb2 100644 --- a/lib/src/sliver_animated_tree.dart +++ b/lib/src/sliver_animated_tree.dart @@ -1,3 +1,5 @@ +import 'dart:collection' show UnmodifiableListView; + import 'package:flutter/material.dart'; import 'sliver_tree.dart'; @@ -80,6 +82,7 @@ class SliverAnimatedTree extends SliverTree { super.key, required super.controller, required super.nodeBuilder, + super.onTreeChanged, this.transitionBuilder = defaultTreeTransitionBuilder, this.duration = const Duration(milliseconds: 300), this.curve = Curves.linear, @@ -181,6 +184,7 @@ class _SliverAnimatedTreeState _flatTree = flatTree; _expansionStatesCache = currentExpansionStates; + widget.onTreeChanged?.call(UnmodifiableListView>(_flatTree)); } void _rebuild() => setState(_updateFlatTree); diff --git a/lib/src/sliver_tree.dart b/lib/src/sliver_tree.dart index 82f9848..0d1d103 100644 --- a/lib/src/sliver_tree.dart +++ b/lib/src/sliver_tree.dart @@ -1,3 +1,5 @@ +import 'dart:collection' show UnmodifiableListView; + import 'package:flutter/material.dart'; import 'tree_controller.dart'; @@ -51,6 +53,7 @@ class SliverTree extends StatefulWidget { super.key, required this.controller, required this.nodeBuilder, + this.onTreeChanged, }); /// {@template flutter_fancy_tree_view.SliverTree.controller} @@ -71,6 +74,24 @@ class SliverTree extends StatefulWidget { /// {@endtemplate} final TreeNodeBuilder nodeBuilder; + /// {@template flutter_fancy_tree_view.SliverTree.onTreeChanged} + /// Called each time the tree gets flattened. + /// + /// Whenever the tree changes in any way and a traversal is required (e.g., + /// when calling [TreeController.rebuild]), this callback is called with the + /// new flat tree. + /// + /// This can be useful when an action needs to be executed as soon as the tree + /// gets rebuilt, like highlighting or scrolling to a selected node. + /// + /// This callback may get called a lot as the expansion states change. + /// + /// Note that for animated tree views, this callback will be called twice + /// for each rebuild, once when the animation begins and one more time when + /// the animation ends. + /// {@endtemplate} + final ValueChanged>>? onTreeChanged; + @override State> createState() => _SliverTreeState(); } @@ -82,6 +103,7 @@ class _SliverTreeState extends State> { final List> flatTree = []; widget.controller.depthFirstTraversal(onTraverse: flatTree.add); _flatTree = flatTree; + widget.onTreeChanged?.call(UnmodifiableListView>(_flatTree)); } void _rebuild() => setState(_updateFlatTree); diff --git a/lib/src/tree_view.dart b/lib/src/tree_view.dart index 0a4ee6b..9c7f946 100644 --- a/lib/src/tree_view.dart +++ b/lib/src/tree_view.dart @@ -44,6 +44,7 @@ class TreeView extends BoxScrollView { super.key, required this.treeController, required this.nodeBuilder, + this.onTreeChanged, super.padding, super.controller, super.primary, @@ -63,11 +64,15 @@ class TreeView extends BoxScrollView { /// {@macro flutter_fancy_tree_view.SliverTree.nodeBuilder} final TreeNodeBuilder nodeBuilder; + /// {@macro flutter_fancy_tree_view.SliverTree.onTreeChanged} + final ValueChanged>>? onTreeChanged; + @override Widget buildChildLayout(BuildContext context) { return SliverTree( controller: treeController, nodeBuilder: nodeBuilder, + onTreeChanged: onTreeChanged, ); } } @@ -109,6 +114,7 @@ class AnimatedTreeView extends TreeView { super.key, required super.treeController, required super.nodeBuilder, + super.onTreeChanged, this.transitionBuilder = defaultTreeTransitionBuilder, this.duration = const Duration(milliseconds: 300), this.curve = Curves.linear, @@ -143,6 +149,7 @@ class AnimatedTreeView extends TreeView { return SliverAnimatedTree( controller: treeController, nodeBuilder: nodeBuilder, + onTreeChanged: onTreeChanged, transitionBuilder: transitionBuilder, duration: duration, curve: curve,