likes
comments
collection
share

[译]Flutter 强大灵活的树形组件 flutter_fancy_tree_view

作者站长头像
站长
· 阅读数 3

Flutter 强大灵活的树形组件 flutter_fancy_tree_view

原文链接:flutter_fancy_tree_view | Flutter Package (flutter-io.cn)

译时版本:1.4.1


flutter_fancy_tree_view

Flutter 组件和 sliver(滑动内容)的集合,可用于将层级数据可视化。

该库使用了回调集以深度优先顺序来遍历层级数据,将所需信息收集到 Dart 的List(树的扁平展示)中, 然后使用 sliver 在屏幕上渲染树的节点。

截图

空白缩进[译]Flutter 强大灵活的树形组件 flutter_fancy_tree_view
连接线[译]Flutter 强大灵活的树形组件 flutter_fancy_tree_view
范围标线[译]Flutter 强大灵活的树形组件 flutter_fancy_tree_view

安装

运行以下命令:

flutter pub add flutter_fancy_tree_view

它会向包的 pubspec.yaml 中添加如下的代码行(会自动运行 flutter pub get):

dependencies:
  flutter_fancy_tree_view: any

现在在 Dart 代码中,可如下使用:

import 'package:flutter_fancy_tree_view/flutter_fancy_tree_view.dart';

⚠️ 警告

请将版本 '1.0' 作为一个全新的包来看待。不鼓励从旧版本升级,因为该包大部分已经重写,并有很多破坏性改动。

特性

  • 动态 "树节点" 模型
  • 可使用任何组件
  • 缩进向导
  • 展开/折叠动画
  • sliver 树变体
  • 支持基本的拖拽

要体验这些特性,可访问 live 示例应用。 示例应用的源代码可在 example 目录中找到。

开始

前往 example/example.md查看一下该包基本用法的示例,示例的注释很完善。 也可以在 example/lib/src/examples 查看一些特定特性的示例。

用法

  1. 创建一个 "树节点" 模型来存储数据
class MyTreeNode {
  const MyTreeNode({
    required this.title,
    this.children = const <MyTreeNode>[],
  });

  final String title;
  final List<MyTreeNode> children;
}
  1. 创建/获取层级数据
final List<MyTreeNode> roots = [
  const MyTreeNode(title: 'My static root node'),
  ...fetchOtherRootNodes(),
];
  1. 初始化树控制器 TreeController
final treeController = TreeController<MyTreeNode>(
  roots: roots,
  childrenProvider: (MyTreeNode node) => node.children,
);

注意: 如查打算使用拖拽特性,确保在 TreeController 中包含 parentProvider 。一些方法如 expandAncestors 和 checkNodeHasAncestor 依赖 parentProvider 运转。不设置时会在调试模式抛出断言错误。

  1. 将控制器传给树视图 TreeView ,然后提供一个组件构建器将数据映射为组件。确保包含树节点展开状态的方式和缩进组件TreeIndentation以适当地缩进它们。
@override
Widget build(BuildContext context) {
  return AnimatedTreeView<MyTreeNode>(
    treeController: treeController,
    nodeBuilder: (BuildContext context, TreeEntry<MyTreeNode> entry) {
      return InkWell(
        onTap: () => treeController.toggleExpansion(entry.node),
        child: TreeIndentation(
          entry: entry,
          child: Text(entry.node.title),
        ),
      );
    },
  );
}

拖拽

对于可用示例,可前往 example/lib/src/examples 目录下的拖拽示例代码。

该包提供了两个新的组件 TreeDraggable 和 TreeDragTarget ,它们封装了 Flutter 的 Draggable 和 DragTarget 组件,添加了一些树视图需要的处理,如悬停时自动展开/折叠的节点、在可滚动的视口组件垂直边缘附近拖拽时自动滚动,等。

现在修改一下前面的例子来包含拖拽特性。

首先,修改"树节点"模型来包含父节点的引用,这是一个重要的步骤,它能确保展开/折叠行为能正常动作。

class MyTreeNode {
  MyTreeNode({
    required this.title,
    Iterable<MyTreeNode>? children,
  }) : children = <MyTreeNode>[] {
    if (children == null) return;

    for (final MyTreeNode child in children) {
      this.children.add(child);

      // 确保更新目标节点的子组件时能更新它的父节点。
      child.parent = this;
    }
  }

  final String title;
  final List<MyTreeNode> children;
  MyTreeNode? parent;
}

修改了树节点模型后,现在确保 TreeController 包含 parentProvider 回调以访问目标树节点的祖先。 对于拖拽特性非常重要,对于方法如 expandAncestors 也很重要。如果 parentProvider 没有定义,则会使用总是返回 null (例,(MyTreeNode node) => null)的回调,并且需要它的方法会在调试模式下会抛出断言错误。

final treeController = TreeController<MyTreeNode>(
  ...,
  parentProvider: (MyTreeNode node) => node.parent,
);

现在,修改树节点组件以包含 TreeDraggable 和 TreeDragTarget 组件。

@override
Widget build(BuildContext context) {
  return AnimatedTreeView<MyTreeNode>(
    treeController: treeController,
    nodeBuilder: (BuildContext context, TreeEntry<MyTreeNode> entry) {
      return TreeDragTarget<MyTreeNode>(
        node: entry.node,
        onNodeAccepted: (TreeDragAndDropDetails details) {
          // 可选,确保目标节点已展开,所以在树重新构建时被拖拽的节点在其附近是可见的。
          treeController.setExpansionState(details.targetNode, true);

          // TODO: 实现树的重排序逻辑

          // 确保重新构建树视图以在新的附近表示重排序后的节点。
          treeController.rebuild();
        },
        builder: (BuildContext context, TreeDragAndDropDetails? details) {
          Widget myTreeNodeTile = Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(entry.node.title),
          );

          // 如果 details 不为空,拖拽的节点会悬停在拖拽目标上。可添加一些反馈给用户。
          if (details != null) {
            myTreeNodeTile = ColoredBox(
              color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
              child: myTreeNodeTile,
            );
          }

          return TreeDraggable<MyTreeNode>(
            node: entry.node,

            // 在拖拽的指针下面向用户表示一些反馈,这可以是任意组件。
            feedback: IntrinsicWidth(
              child: Material(
                elevation: 4,
                child: myTreeNodeTile,
              ),
            ),

            child: InkWell(
              onTap: () => treeController.toggleExpansion(entry.node),
              child: TreeIndentation(
                entry: entry,
                child: myTreeNodeTile,
              ),
            ),
          );
        },
      );
    },
  );
}

API 文档

前往 pub.dev api 文档


转载自:https://juejin.cn/post/7291133703835779130
评论
请登录