Flutter递归树结构展示插件recursive_tree_flutter的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter递归树结构展示插件recursive_tree_flutter的使用

Table of Contents

简介

recursive_tree_flutter 是一个用于构建和可视化树数据结构的Flutter库。它不仅关注UI界面,更注重树数据结构本身,支持多种特殊的UI样式。例如,它可以更新树节点、返回选中的节点列表或叶子节点列表等。

代码示例

下面是一个简单的例子,展示了如何使用 recursive_tree_flutter 构建一个单选可扩展树:

import 'package:flutter/material.dart';
import 'package:recursive_tree_flutter/recursive_tree_flutter.dart';

class ExTreeSingleChoice extends StatefulWidget {
  const ExTreeSingleChoice({super.key});

  @override
  State<ExTreeSingleChoice> createState() => _ExTreeSingleChoiceState();
}

class _ExTreeSingleChoiceState extends State<ExTreeSingleChoice> {
  late TreeType<CustomNodeType> _tree;
  final TextEditingController _textController = TextEditingController();

  @override
  void initState() {
    _tree = sampleTree();
    super.initState();
  }

  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
      child: Scaffold(
        appBar: AppBar(
          title: const Text("Example Single Choice Expandable Tree"),
        ),
        body: Column(
          children: [
            Expanded(
              flex: 4,
              child: SingleChildScrollView(
                child: _VTSNodeWidget(
                  _tree,
                  onNodeDataChanged: () => setState(() {}),
                ),
              ),
            ),
            Expanded(
              flex: 1,
              child: TextFormField(
                controller: _textController,
                decoration: const InputDecoration(
                  hintText: "PRESS ENTER TO UPDATE",
                ),
                onFieldSubmitted: (value) {
                  updateTreeWithSearchingTitle(_tree, value);
                  setState(() {});
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _VTSNodeWidget extends StatefulWidget {
  const _VTSNodeWidget(
    this.tree, {
    required this.onNodeDataChanged,
  });

  final TreeType<CustomNodeType> tree;
  final VoidCallback onNodeDataChanged;

  @override
  State<_VTSNodeWidget> createState() => _VTSNodeWidgetState();
}

class _VTSNodeWidgetState<T extends AbsNodeType> extends State<_VTSNodeWidget>
    with SingleTickerProviderStateMixin, ExpandableTreeMixin<CustomNodeType> {
  final Tween<double> _turnsTween = Tween<double>(begin: -0.25, end: 0.0);

  @override
  initState() {
    super.initState();
    initTree();
    initRotationController();
    if (tree.data.isExpanded) {
      rotationController.forward();
    }
  }

  @override
  void initTree() {
    tree = widget.tree;
  }

  @override
  void initRotationController() {
    rotationController = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );
  }

  @override
  void dispose() {
    disposeRotationController();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => buildView();

  @override
  Widget buildNode() {
    if (!widget.tree.data.isShowedInSearching) return const SizedBox.shrink();

    return InkWell(
      onTap: updateStateToggleExpansion,
      child: Row(
        children: [
          buildRotationIcon(),
          Expanded(child: buildTitle()),
          buildTrailing(),
        ],
      ),
    );
  }

  Widget buildRotationIcon() {
    return RotationTransition(
      turns: _turnsTween.animate(rotationController),
      child: tree.isLeaf
          ? Container()
          : IconButton(
              iconSize: 16,
              icon: const Icon(Icons.expand_more, size: 16.0),
              onPressed: updateStateToggleExpansion,
            ),
    );
  }

  Widget buildTitle() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 6.0),
      child: Text(
        tree.data.title + (tree.isLeaf ? "" : " (${tree.children.length})"),
        maxLines: 1,
        overflow: TextOverflow.ellipsis,
      ),
    );
  }

  Widget buildTrailing() {
    if (tree.data.isUnavailable) {
      return const Icon(Icons.close_rounded, color: Colors.red);
    }

    if (tree.isLeaf) {
      return Checkbox(
        value: tree.data.isChosen!,
        onChanged: (value) {
          updateTreeSingleChoice(tree, !tree.data.isChosen!);
          widget.onNodeDataChanged();
        },
      );
    }

    return const SizedBox.shrink();
  }

  @override
  List<Widget> generateChildrenNodesWidget(List<TreeType<CustomNodeType>> list) =>
      List.generate(
        list.length,
        (int index) => _VTSNodeWidget(
          list[index],
          onNodeDataChanged: widget.onNodeDataChanged,
        ),
      );

  @override
  void updateStateToggleExpansion() => setState(() => toggleExpansion());
}

特性

  • 构建树数据结构(Dart代码)。
  • 提供多种树操作函数,如查找节点、文本搜索、更新多选/单选树等。
  • 支持懒加载以在运行时展开树。
  • 树数据结构可以独立于Flutter UI使用。
  • 使用Flutter可视化树结构。
  • 允许自定义Flutter UI以满足特定需求。

内容

树数据结构 (Dart代码)

树数据结构包括内节点和叶节点,类似于计算机上的目录树结构。主要类如下:

  • AbsNodeType:抽象类,表示节点的数据类型。
  • TreeType:树数据结构,包含节点数据和子节点列表。

辅助函数 (Dart代码)

提供了多个与树遍历和更新相关的函数,如:

  • isChosenAll(tree):检查当前树的所有子节点是否被选中。
  • findRoot(tree):查找根节点。
  • searchAllTreesWithTitleDFS(tree, text, result):使用DFS算法搜索包含特定文本的节点。

Flutter UI 树

提供了多种UI树组件,如:

  • StackWidget:使用堆栈方法构建的UI树。
  • ExpandableTreeWidget:使用可扩展方法构建的UI树。
  • VTSDepartmentTreeWidget:另一个使用可扩展方法构建的UI树。

基于 ExpandableTreeMixin 的可扩展树的工作原理

可扩展UI树的结构如下:

SingleChildScrollView( // 树是可滚动的
  - NodeWidget (root)
    -- NodeWidget
      +++ NodeWidget
      +++ NodeWidget
      +++ NodeWidget
    -- NodeWidget
      +++ NodeWidget
    ...
)

NodeWidget 是一个递归构建的 StatefulWidget,包裹在 SingleChildScrollView 中以提供滚动功能。更新树(数据)将改变 NodeWidget 的状态/UI。NodeWidget 继承了 ExpandableTreeMixin,提供了初始化树、旋转控制器、构建节点等功能。

BSD-3-Clause License

该库遵循 BSD-3-Clause 许可证,允许自由分发和使用源代码和二进制形式,但需保留版权声明和免责声明。


通过以上内容,您可以更好地理解和使用 recursive_tree_flutter 插件来构建和展示递归树结构。希望这些信息对您有所帮助!


更多关于Flutter递归树结构展示插件recursive_tree_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter递归树结构展示插件recursive_tree_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用recursive_tree_flutter插件来展示递归树结构的代码示例。

首先,确保你已经在pubspec.yaml文件中添加了recursive_tree_flutter插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  recursive_tree_flutter: ^最新版本号  # 替换为实际最新版本号

然后,运行flutter pub get来安装依赖。

接下来是一个完整的示例,展示如何使用recursive_tree_flutter插件来展示递归树结构。

main.dart

import 'package:flutter/material.dart';
import 'package:recursive_tree_flutter/recursive_tree_flutter.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Recursive Tree Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 示例树数据
  final List<TreeNode> treeData = [
    TreeNode(
      title: 'Root',
      children: [
        TreeNode(
          title: 'Child 1',
          children: [
            TreeNode(title: 'Grandchild 1'),
            TreeNode(title: 'Grandchild 2'),
          ],
        ),
        TreeNode(
          title: 'Child 2',
          children: [
            TreeNode(title: 'Grandchild 3'),
          ],
        ),
      ],
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Recursive Tree Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: RecursiveTreeView(
          nodes: treeData,
          nodeBuilder: (context, nodeData) {
            return ListTile(
              title: Text(nodeData.title),
              trailing: Icon(Icons.arrow_drop_down),
              onTap: () {
                // Handle node tap if needed
              },
            );
          },
        ),
      ),
    );
  }
}

// TreeNode 数据模型
class TreeNode {
  String title;
  List<TreeNode> children;

  TreeNode({required this.title, this.children = const []});
}

解释

  1. 依赖添加:在pubspec.yaml中添加recursive_tree_flutter依赖。
  2. 数据模型:定义一个TreeNode类来表示树的节点,每个节点有一个标题和可选的子节点列表。
  3. UI 组件
    • 使用RecursiveTreeView组件来展示树结构。
    • nodes属性传入树的根节点列表。
    • nodeBuilder属性用于自定义每个节点的渲染方式,这里使用ListTile来显示节点标题和一个下拉图标。

这个示例展示了如何基本使用recursive_tree_flutter插件来渲染一个递归的树结构。你可以根据需要进一步自定义节点的渲染逻辑和交互行为。

回到顶部