Repository: bdlukaa/fluent_ui

Issue:
🐛 TreeViewItem.from does not set parent on copied children
**Describe the bug**
The `TreeViewItem.from` factory constructor that makes a deep copy of itself and its children does not set the parent property of the children properly. This can break things in subtle ways on the tree data before the tree data is passed into the first widget build.

**Additional context**
I'll open a PR that fixes this.




Fix patch:
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<TreeViewItem> {
     if (isNotEmpty) {
       final list = <TreeViewItem>[];
       final anyExpandableSiblings = any((i) => i.isExpandable);
-      for (final item in [...this]) {
+      for (final item in this) {
         item
           .._parent = parent
           .._anyExpandableSiblings = anyExpandableSiblings;


Test patch:
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]);
+  });
 }