Type mismatch on generic Nodes
You use generic Node<T> but you never address this T type in your code. That is why sometimes TypeError is occurred.
For example I have a function:
void _onNodeSelected(Node<Zone> node) {
...
}
When this code is executing
final node = controller.getNode('key');
_onNodeSelected(node); // Crash is here because Node<Zone> is expected but Node<dynamic> is provided.
Please respect the type T everywhere where Node type is used, i.e. Node<T> instead of Node.
P.S.: Also you created `nodeBuilder` (thanks for that!) but you use
Function (BuildContext context, Node node)
and it is better to use:
Widget Function (BuildContext context, Node node)
Comments (15)
-
-
Thanks for the suggestion on using
Widget Function(BuildContext context, Node node)
! -
reporter If you define your type
Node<Zone>
, not justNode
, when you create the node,getNode
will returnNode<Zone>
and notNode<dynamic>
.Unfortunately it is not true. And also `copyWith` returns Node<dynamic> instead of Node<Zone> but I clonned
Node<Zone>
.
-
Gustav, can you provide me with a code sample where this is not true for you. I am willing to make the changes.
-
reporter Kevin, unfortunately I cannot copy my code as I work via VPN (w/o Internet) so I can just provide some lines from my real code:
// Somewhere in StreamBuilder's builder: final zones = snapshot.data.items; // zones is a List<Zone> final treeNodes = _buildTree(zones); // treeNodes is a List<Node<Zone>> treeController = TreeViewController( children: treeNodes, // Here children already a List<Node<dynamic>> selectedKey: _selectedKey, ); return TreeView( controller: treeControoller, ... onNodeTap: _nodeSelectHandler, ); // Node selection handler Future<void> _nodeSelectHandler(String key) async { final node = treeController.getNode(key); // already here `node` is Node<dynamic> final updatedZone = await zoneBloc.getEntity((node.data as Zone).id); widget.onNodeChanged?.call(node.cast(updateZone)); // Here I am forced to call a `cast` method. Otherwise I have a TypeError because the `onNodeChanged` is ValueChanged<Node<Zone>> } // extension NodeExtension<T> on Node<T> { Node<T> cast([T data]) => Node<T>( key: key, label: label, ... data: data ?? this.data, ); }
-
Please respect the type T everywhere where Node type is used, i.e. Node<T> instead of Node.
I also agree with that
-
Also having this issue when using copyWith it returns Node<dynamic> rather than Node<T>. Please add all generic types as in the example…
Please change this
Node<T> copyWith({ String? key, String? label, List<Node<T>>? children, bool? expanded, bool? parent, IconData? icon, T? data, }) => Node<T>( key: key ?? this.key, label: label ?? this.label, icon: icon ?? this.icon, expanded: expanded ?? this.expanded, parent: parent ?? this.parent, children: children ?? this.children, data: data ?? this.data, );
-
- changed status to resolved
Fixes #32: Updated Node copyWith method to properly use Generic for children nodes Fixes
#44: Removed hardcoded hoverColor when creating the TreeView widget→ <<cset 104d77b8c2d3>>
-
Thanks for this update Jennie. I am updating the code now.
-
Thanks so much Kevin. While you are in the TreeViewController class…there were quite a few other references that return Node rather than Node<T> if you can fix those too?
Thanks again!!
-
I moved too fast Jennie. I already published those updates.
I think I mentioned this previously but unless I’m missing something, there is no way for the TreeViewController to know the generic T so I can’t add it throughout without providing a means for you to pass that generic at method invocation. e.g.
var node = controller.getNode<double>(key);
-
Hi Kevin,
You can pass T through on the method. If you are creating a Node without a type (such as copyWith()) inside TreeViewController it defeats the purpose of a strongly typed Node.
For example in copyWith you could change the method signature to →
Node copyWith<T>({ })
And then you call it with:
copyWith<String>();
If you are calling it within TreeViewController you need to make sure all method sigs pass through the type T. For example for updateNode it would look like this:
List<Node<T>> updateNode<T>(String key, Node<T> newNode, {Node<T>? parent}) { List<Node<T>> _children = parent == null ? this.children : parent.children; return _children.map((Node<T> child) { if (child.key == key) { return newNode; } else { if (child.isParent) { return child.copyWith<T>( children: updateNode<T>( key, newNode, parent: child, ), ); } return child; } }).toList();
The root issue I am trying to address it when we lose the typed Node during a copyWith (basically where you created a Node() without a type.)
Thanks Kevin!
-
Thanks Jennie, that certainly clears up what Gustav was trying to convey. And it’s something I didn’t think about when I added the
data
property. I will work on getting that updated ASAP.
-
Thanks Kevin!!
-
- changed status to open
- Log in to comment
Hi Gustav, The T type is addressed in the place where it is defined: on the Node class. In order to use
Node<T>
throughout the code, I would need to have and defineT
throughout the code. That isn’t feasible.If you define your type
Node<Zone>
, not justNode
, when you create the node,getNode
will returnNode<Zone>
and notNode<dynamic>
.