From 72efefaa4955aca2393d1573d3e6dfb43e25bd1d Mon Sep 17 00:00:00 2001 From: Klondike Dragon Date: Tue, 19 Mar 2024 22:49:45 -0600 Subject: [PATCH] TreeViewItem.from should set parent properly * Ensures that deep copies of a tree have proper linkage. * Adds a unit test to verify that the parent prop is set properly. --- lib/src/controls/navigation/tree_view.dart | 45 ++++++++++++--------- test/tree_view_test.dart | 47 ++++++++++++++++++++++ 2 files changed, 72 insertions(+), 20 deletions(-) diff --git a/lib/src/controls/navigation/tree_view.dart b/lib/src/controls/navigation/tree_view.dart index c7090992c..95c353a74 100644 --- a/lib/src/controls/navigation/tree_view.dart +++ b/lib/src/controls/navigation/tree_view.dart @@ -184,25 +184,30 @@ class TreeViewItem with Diagnosticable { /// its child items. Useful if you want to have multiple trees with the /// same items, but with different UX states (e.g., selection, visibility, /// etc.). - TreeViewItem.from(TreeViewItem source) - : this( - key: source.key, - leading: source.leading, - content: source.content, - value: source.value, - children: source.children.map(TreeViewItem.from).toList(), - collapsable: source.collapsable, - expanded: source.expanded, - selected: source.selected, - onInvoked: source.onInvoked, - onExpandToggle: source.onExpandToggle, - backgroundColor: source.backgroundColor, - autofocus: source.autofocus, - focusNode: source.focusNode, - semanticLabel: source.semanticLabel, - loadingWidget: source.loadingWidget, - lazy: source.lazy, - ); + factory TreeViewItem.from(TreeViewItem source) { + final newItem = TreeViewItem( + key: source.key, + leading: source.leading, + content: source.content, + value: source.value, + children: source.children.map(TreeViewItem.from).toList(), + collapsable: source.collapsable, + expanded: source.expanded, + selected: source.selected, + onInvoked: source.onInvoked, + onExpandToggle: source.onExpandToggle, + backgroundColor: source.backgroundColor, + autofocus: source.autofocus, + focusNode: source.focusNode, + semanticLabel: source.semanticLabel, + loadingWidget: source.loadingWidget, + lazy: source.lazy, + ); + for (final c in newItem.children) { + c._parent = newItem; + } + return newItem; + } /// Whether this node is expandable bool get isExpandable { @@ -385,7 +390,7 @@ extension TreeViewItemCollection on List { if (isNotEmpty) { final list = []; final anyExpandableSiblings = any((i) => i.isExpandable); - for (final item in [...this]) { + for (final item in this) { item .._parent = parent .._anyExpandableSiblings = anyExpandableSiblings; diff --git a/test/tree_view_test.dart b/test/tree_view_test.dart index f36c700e1..5cf30f318 100644 --- a/test/tree_view_test.dart +++ b/test/tree_view_test.dart @@ -29,6 +29,53 @@ void main() { await tester.pumpWidget(wrapApp(child: TreeView(items: items))); expect(itemOne.parent, isNotNull); + expect(itemOne.parent, items[0]); expect(itemOne.parent?.selected, null); }); + + testWidgets('TreeViewItem deep copy rebuilds parent linkage', + (WidgetTester tester) async { + final items = [ + TreeViewItem( + content: const Text('Parent item'), + children: [ + TreeViewItem( + content: const Text('Item 1'), + children: [ + TreeViewItem( + content: const Text('Subitem 1'), + ), + TreeViewItem( + content: const Text('Subitem 2'), + ), + ], + ), + TreeViewItem( + content: const Text('Item 2'), + ), + TreeViewItem( + content: const Text('Item 3'), + ), + ], + ), + ]; + + await tester.pumpWidget(wrapApp(child: TreeView(items: items))); + expect(items[0].parent, isNull); + expect(items[0].children[0].parent, items[0]); + expect(items[0].children[1].parent, items[0]); + expect(items[0].children[2].parent, items[0]); + expect(items[0].children[0].children[0].parent, items[0].children[0]); + expect(items[0].children[0].children[1].parent, items[0].children[0]); + + final itemsCopy = items.map(TreeViewItem.from).toList(); + expect(itemsCopy[0].parent, isNull); + expect(itemsCopy[0].children[0].parent, itemsCopy[0]); + expect(itemsCopy[0].children[1].parent, itemsCopy[0]); + expect(itemsCopy[0].children[2].parent, itemsCopy[0]); + expect( + itemsCopy[0].children[0].children[0].parent, itemsCopy[0].children[0]); + expect( + itemsCopy[0].children[0].children[1].parent, itemsCopy[0].children[0]); + }); }