baumths / flutter_tree_view

A Flutter collection of widgets and slivers that helps bringing your hierarchical data to life.
https://baumths.github.io/flutter_tree_view
MIT License
181 stars 69 forks source link

Expand All or scroll to node? #30

Closed jaxnz closed 2 years ago

jaxnz commented 2 years ago

Is it possible to expand all nodes, or scroll to one?

It looks like #7 has been implemented, but can't figure out how it works.

baumths commented 2 years ago

Hey @jaxnz, thank your for using the package!

To expand all nodes you can use [TreeViewController.expandAll].

Scrolling to a node is not supported currently, but you could implement it yourself by passing a ScrollController to the treeview and calculating the offset to scroll to using the height and index of a node.

jaxnz commented 2 years ago

Thanks, was able to do it like this for anyone else that may be wondering:

  void scrollTo(TreeNode node) {
    final offset = treeController.indexOf(node) * 30.0;
    scrollController!.animateTo(
      offset,
      duration: const Duration(milliseconds: 500),
      curve: Curves.ease,
    );
  }
mstich-mcmservices commented 1 year ago

@baumths If we did want to implement our own scroll to node, is there a way to get the height of each of the currently visible tree nodes?

baumths commented 1 year ago

Hey @mstich-mcmservices, thank you for using this package!

Checkout this article from the gskinner team about measuring a widget's size: https://blog.gskinner.com/archives/2021/01/flutter-how-to-measure-widgets.html

Using the RenderBox approach:

class MeasureSizeRenderObject extends RenderProxyBox {
  MeasureSizeRenderObject(this.onChange);
  void Function(Size size) onChange;

  Size? _prevSize;
  @override
  void performLayout() {
    super.performLayout();
    Size newSize = child!.size;
    if (_prevSize == newSize) return;
    _prevSize = newSize;
    WidgetsBinding.instance.addPostFrameCallback((_) => onChange(newSize));
  }
}

class MeasurableWidget extends SingleChildRenderObjectWidget {
  const MeasurableWidget({
    super.key,
    required this.onChange,
    required this.onUnmount,
    required super.child,
  });

  final ValueChanged<Size> onChange;
  final VoidCallback onUnmount;

  @override
  RenderObject createRenderObject(BuildContext context) {
    return MeasureSizeRenderObject(onChange);
  }

  @override
  void didUnmountRenderObject(covariant MeasureSizeRenderObject renderObject) {
    super.didUnmountRenderObject(renderObject);
    onUnmount();
  }
}

Usage example:

// On the StatefulWidget that builds your TreeView
final Map<Object, Size> nodeToSize = {};

Widget nodebuilder(BuildContext context, TreeEntry<Object> entry) {
  return MeasurableWidget(
    onChange: (Size size) {
      nodeToSize[entry.node] = size;
    },
    onUnmount: () {
      nodeToSize.remove(entry.node);
    },
    child: YourNodeTile(entry: entry),
  );
}

After the first frame, nodeToSize.values will have the size of the widgets that are currently built, i.e., viewport + cacheExtent.

mstich-mcmservices commented 1 year ago

@baumths Thanks for the example code! I will give it a try and see how it works. I started working with your tree package prior to the big 1.x.x rewrite and I'm now in the process of refactoring to handle moving to 1.4.0. Thank you for your great contribution to the flutter community!