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 在屏幕上渲染树的节点。
截图
空白缩进 | |
连接线 | |
范围标线 |
安装
运行以下命令:
csharp
flutter pub add flutter_fancy_tree_view
它会向包的 pubspec.yaml 中添加如下的代码行(会自动运行 flutter pub get
):
yaml
dependencies:
flutter_fancy_tree_view: any
现在在 Dart 代码中,可如下使用:
arduino
import 'package:flutter_fancy_tree_view/flutter_fancy_tree_view.dart';
⚠️ 警告
请将版本 '1.0' 作为一个全新的包来看待。不鼓励从旧版本升级,因为该包大部分已经重写,并有很多破坏性改动。
特性
- 动态 "树节点" 模型
- 可使用任何组件
- 缩进向导
- 展开/折叠动画
- sliver 树变体
- 支持基本的拖拽
要体验这些特性,可访问 live 示例应用。 示例应用的源代码可在 example目录中找到。
开始
前往 example/example.md查看一下该包基本用法的示例,示例的注释很完善。 也可以在 example/lib/src/examples 查看一些特定特性的示例。
用法
- 创建一个 "树节点" 模型来存储数据
kotlin
class MyTreeNode {
const MyTreeNode({
required this.title,
this.children = const <MyTreeNode>[],
});
final String title;
final List<MyTreeNode> children;
}
- 创建/获取层级数据
csharp
final List<MyTreeNode> roots = [
const MyTreeNode(title: 'My static root node'),
...fetchOtherRootNodes(),
];
- 初始化树控制器 TreeController。
ini
final treeController = TreeController<MyTreeNode>(
roots: roots,
childrenProvider: (MyTreeNode node) => node.children,
);
注意: 如查打算使用拖拽特性,确保在
TreeController
中包含parentProvider
。一些方法如expandAncestors
和checkNodeHasAncestor
依赖parentProvider
运转。不设置时会在调试模式抛出断言错误。
- 将控制器传给树视图 TreeView ,然后提供一个组件构建器将数据映射为组件。确保包含树节点展开状态的方式和缩进组件TreeIndentation以适当地缩进它们。
less
@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 组件,添加了一些树视图需要的处理,如悬停时自动展开/折叠的节点、在可滚动的视口组件垂直边缘附近拖拽时自动滚动,等。
现在修改一下前面的例子来包含拖拽特性。
首先,修改"树节点"模型来包含父节点的引用,这是一个重要的步骤,它能确保展开/折叠行为能正常动作。
kotlin
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
)的回调,并且需要它的方法会在调试模式下会抛出断言错误。
ini
final treeController = TreeController<MyTreeNode>(
...,
parentProvider: (MyTreeNode node) => node.parent,
);
现在,修改树节点组件以包含 TreeDraggable
和 TreeDragTarget
组件。
less
@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 文档。