From d5538f259e65f9330aa8c25c0c6f67682b0f44fb Mon Sep 17 00:00:00 2001 From: Stefan Schindler Date: Thu, 20 Mar 2025 15:40:48 +0100 Subject: [PATCH 1/3] add maxSimultaneousDrags param --- lib/src/drag_and_drop.dart | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/src/drag_and_drop.dart b/lib/src/drag_and_drop.dart index d5bb095..ab96c49 100644 --- a/lib/src/drag_and_drop.dart +++ b/lib/src/drag_and_drop.dart @@ -60,6 +60,7 @@ class TreeDraggable extends StatefulWidget { this.hitTestBehavior = HitTestBehavior.deferToChild, this.longPressDelay, this.longPressHapticFeedbackOnStart = true, + this.maxSimultaneousDrags = 1, }); /// The widget below this widget in the tree. @@ -256,6 +257,16 @@ class TreeDraggable extends StatefulWidget { /// Defaults to `null`. final Duration? longPressDelay; + /// How many simultaneous drags to support. + /// + /// When null, no limit is applied. Set this to 1 if you want to only allow + /// the drag source to have one item dragged at a time. Set this to 0 if you + /// want to prevent the draggable from actually being dragged. + /// + /// If you set this property to 1, consider supplying an "empty" widget for + /// [childWhenDragging] to create the illusion of actually moving [child]. + final int? maxSimultaneousDrags; + @override State> createState() => _TreeDraggableState(); } @@ -378,7 +389,7 @@ class _TreeDraggableState extends State> if (widget.longPressDelay != null) { return LongPressDraggable( data: widget.node, - maxSimultaneousDrags: 1, + maxSimultaneousDrags: widget.maxSimultaneousDrags, onDragStarted: onDragStarted, onDragUpdate: onDragUpdate, onDraggableCanceled: onDraggableCanceled, @@ -399,7 +410,7 @@ class _TreeDraggableState extends State> return Draggable( data: widget.node, - maxSimultaneousDrags: 1, + maxSimultaneousDrags: widget.maxSimultaneousDrags, onDragStarted: onDragStarted, onDragUpdate: onDragUpdate, onDraggableCanceled: onDraggableCanceled, From 92c40922772661c1b6375903e33623c8863289b4 Mon Sep 17 00:00:00 2001 From: Stefan Schindler Date: Fri, 21 Mar 2025 12:58:08 +0100 Subject: [PATCH 2/3] replace roots with root to allow filtering top level --- example/lib/src/examples/drag_and_drop.dart | 2 +- example/lib/src/examples/filterable.dart | 2 +- example/lib/src/examples/lazy_loading.dart | 2 +- example/lib/src/examples/minimal.dart | 2 +- lib/src/tree_controller.dart | 20 ++++++++++++-------- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/example/lib/src/examples/drag_and_drop.dart b/example/lib/src/examples/drag_and_drop.dart index 5429425..8ea6a24 100644 --- a/example/lib/src/examples/drag_and_drop.dart +++ b/example/lib/src/examples/drag_and_drop.dart @@ -85,7 +85,7 @@ class _DragAndDropTreeViewState extends State { populateExampleTree(root); treeController = TreeController( - roots: root.children, + root: root, childrenProvider: (Node node) => node.children, // The parentProvider is extremely important when automatically expanding diff --git a/example/lib/src/examples/filterable.dart b/example/lib/src/examples/filterable.dart index e860d8c..4231646 100644 --- a/example/lib/src/examples/filterable.dart +++ b/example/lib/src/examples/filterable.dart @@ -85,7 +85,7 @@ class _FilterableTreeViewState extends State { populateExampleTree(root); treeController = TreeController( - roots: root.children, + root: root, childrenProvider: getChildren, )..expandAll(); diff --git a/example/lib/src/examples/lazy_loading.dart b/example/lib/src/examples/lazy_loading.dart index 6cf910e..434d3af 100644 --- a/example/lib/src/examples/lazy_loading.dart +++ b/example/lib/src/examples/lazy_loading.dart @@ -98,7 +98,7 @@ class _LazyLoadingTreeViewState extends State { void initState() { super.initState(); treeController = TreeController( - roots: childrenProvider(Data.root), + root: Data.root, childrenProvider: childrenProvider, ); } diff --git a/example/lib/src/examples/minimal.dart b/example/lib/src/examples/minimal.dart index 14a4364..7c67908 100644 --- a/example/lib/src/examples/minimal.dart +++ b/example/lib/src/examples/minimal.dart @@ -32,7 +32,7 @@ class _MinimalTreeViewState extends State { }); treeController = TreeController( - roots: root.children, + root: root, childrenProvider: (Node node) => node.children, ); } diff --git a/lib/src/tree_controller.dart b/lib/src/tree_controller.dart index 66e6a77..db1ab72 100644 --- a/lib/src/tree_controller.dart +++ b/lib/src/tree_controller.dart @@ -97,11 +97,11 @@ class TreeController with ChangeNotifier { /// [TreeController.parentProvider] is set to a callback that always returns /// null. TreeController({ - required Iterable roots, + required T root, required this.childrenProvider, ParentProvider? parentProvider, this.defaultExpansionState = false, - }) : _roots = roots { + }) : _root = root { assert(() { _debugHasParentProvider = parentProvider != null; return true; @@ -112,11 +112,16 @@ class TreeController with ChangeNotifier { /// The roots of the tree. /// /// These nodes are used as a starting point when traversing the tree. - Iterable get roots => _roots; - Iterable _roots; - set roots(Iterable nodes) { - if (nodes == _roots) return; - _roots = nodes; + Iterable get roots => childrenProvider(root); + + /// The root of the tree. + /// + /// This node is used as a starting point when traversing the tree. + T get root => _root; + T _root; + set root(T node) { + if (node == _root) return; + _root = node; rebuild(); } @@ -615,7 +620,6 @@ class TreeController with ChangeNotifier { @override void dispose() { - _roots = const Iterable.empty(); toggledNodes.clear(); super.dispose(); } From bc5c45ad46515568ff4b51407636798e3d28b97f Mon Sep 17 00:00:00 2001 From: Stefan Schindler Date: Fri, 11 Apr 2025 13:01:10 +0200 Subject: [PATCH 3/3] ConnectingLines IntendGuide: add optional fixedVerticalOffset param --- lib/src/tree_indentation.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/src/tree_indentation.dart b/lib/src/tree_indentation.dart index bf2ea0f..e2cc94d 100644 --- a/lib/src/tree_indentation.dart +++ b/lib/src/tree_indentation.dart @@ -152,6 +152,7 @@ class IndentGuide { PathModifier? pathModifier, bool roundCorners, bool connectBranches, + double? fixedVerticalOffset, }) = ConnectingLinesGuide; /// Convenient constructor to create a [ScopingLinesGuide]. @@ -522,6 +523,7 @@ class ConnectingLinesGuide extends AbstractLineGuide { super.pathModifier, this.roundCorners = false, this.connectBranches = false, + this.fixedVerticalOffset, }); /// Decides if the connection between vertical and horizontal lines should be @@ -543,6 +545,9 @@ class ConnectingLinesGuide extends AbstractLineGuide { /// Defaults to `false`. final bool connectBranches; + /// TODO + final double? fixedVerticalOffset; + @override CustomPainter createPainter(BuildContext context, TreeEntry entry) { return _ConnectingLinesPainter( @@ -567,6 +572,7 @@ class ConnectingLinesGuide extends AbstractLineGuide { PathModifier? Function()? pathModifier, bool? roundCorners, bool? connectBranches, + double? fixedVerticalOffset, }) { return ConnectingLinesGuide( indent: indent ?? this.indent, @@ -580,6 +586,7 @@ class ConnectingLinesGuide extends AbstractLineGuide { pathModifier: pathModifier != null ? pathModifier() : this.pathModifier, roundCorners: roundCorners ?? this.roundCorners, connectBranches: connectBranches ?? this.connectBranches, + fixedVerticalOffset: fixedVerticalOffset ?? fixedVerticalOffset, ); } @@ -671,7 +678,7 @@ class _ConnectingLinesPainter extends CustomPainter { } // Add connections - final double y = size.height * 0.5; + final double y = guide.fixedVerticalOffset ?? size.height * 0.5; path.moveTo(connectionStart, 0.0); if (guide.roundCorners) {