Flutter macOS树形视图插件macos_tree_view的使用

Flutter macOS树形视图插件macos_tree_view的使用

macos_tree_view

macOS风格的树形视图组件。


🛠️ 安装

在你的 pubspec.yaml 文件中添加依赖:

dependencies:
  macos_tree_view: ^0.3.0

❓ 为什么需要另一个树形视图?

  • 高性能:无论树形结构有多深,都能保持良好的性能。
  • 完全控制:提供了 TreeViewController 来完全控制树形视图。

🎉 大力感谢

感谢这些优秀的公司支持开源开发者 ❤

GitHub


📄 许可证

MIT

版权所有 © 2022-present, SianLoong Lee


示例代码

以下是一个完整的示例代码,展示如何使用 macos_tree_view 插件创建一个树形视图。

示例代码:example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:macos_tree_view/macos_tree_view.dart';
import 'package:uuid/uuid.dart';

class Test {
  String name;
  List<Test> children;

  Test({required this.name, required this.children});
}

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const TabPage(),
    );
  }
}

class TabPage extends StatefulWidget {
  const TabPage({Key? key}) : super(key: key);

  [@override](/user/override)
  State<TabPage> createState() => _TabPageState();
}

class _TabPageState extends State<TabPage> with SingleTickerProviderStateMixin {
  late TreeViewController<String> _treeViewController;
  late TabController _controller;
  late TextEditingController _textEditingController;

  List<Node<String>> nodes = [
    Node.fromLabel('Label A', key: const Key('a')),
    Node.fromLabel('Label B', key: const Key('b'), children: [
      Node.fromLabel('B1', key: const Key('b1'), children: [
        Node.fromLabel('B1.1', key: const Key('b1.1')),
        Node.fromLabel('B1.2', key: const Key('b1.2')),
        Node.fromLabel('B1.3', key: const Key('b1.3')),
      ]),
      Node.fromLabel('B2', key: const Key('b2')),
      Node.fromLabel('B3', key: const Key('b3')),
    ]),
    Node(
      key: const Key('c'),
      label: 'Label C (expandable)',
      expanded: false,
      data: '1',
      children: [
        Node.fromLabel('Label C.A', key: const Key('ca')),
        Node.fromLabel('Label C.B', key: const Key('cb')),
        Node(
          key: const Key('c.c'),
          label: 'Label C.C (expandable)',
          expanded: false,
          data: '1',
          children: [
            Node.fromLabel('Label C.C.A', key: const Key('c.c.a')),
            Node.fromLabel('Label C.C.B', key: const Key('c.c.b')),
            Node.fromLabel('Label C.C.C', key: const Key('c.c.c')),
            Node.fromLabel('Label C.C.D', key: const Key('c.c.d')),
            Node.fromLabel('Label C.C.E', key: const Key('c.c.e')),
          ],
        ),
      ],
    ),
    Node.fromLabel('Label D', key: const Key('d')),
    Node.fromLabel('Label E', key: const Key('e')),
    Node(
      label: 'Label Z',
      key: const Key('z'),
      children: [
        Node.fromLabel('Label Z.A', key: const Key('za')),
      ],
    ),
  ];

  [@override](/user/override)
  void initState() {
    super.initState();

    // 初始化数据
    final List<Node<String>> iRow = [];
    for (var i = 0; i < 100; i++) {
      var uuid1 = const Uuid();
      final List<Node<String>> jRow = [];
      for (var j = 0; j < 100; j++) {
        var uuid2 = const Uuid();
        jRow.add(Node.fromLabel('$uuid2'));
      }
      iRow.add(Node.fromLabel('$uuid1', children: jRow));
    }

    nodes.addAll(iRow);

    _textEditingController = TextEditingController(text: 'hello');
    _treeViewController = TreeViewController(
      selectedValues: {const Key('z')},
      children: nodes,
    );

    _controller = TabController(vsync: this, length: 2);

    _delay();
  }

  Future<void> _delay() async {
    const duration = Duration(seconds: 2);

    await Future.delayed(duration);
    print('============= addNode `jk`! =================');
    _treeViewController.addNode(Node.fromLabel('Batman & Joker'));
    _treeViewController.addNode(
        Node.fromLabel('This is a very very long labelllllllllllllllllll!'));

    await Future.delayed(duration);
    print('============= addNode "z"! =================');
    _treeViewController.addNode(Node.fromLabel('Junkai'));
    _treeViewController.addNode(
        Node.fromLabel('z clone', key: const Key('z.clone')),
        parent: const Key('z'),
        mode: InsertMode.prepend);
    print('============= addNode "nested deep c"! =================');
    _treeViewController.addNode(
        Node.fromLabel('Nested Deep C', key: const Key('nested.deepc')),
        parent: const Key('c.c.c'));
    _treeViewController.addNode(
        Node.fromLabel('z clone (prepend)', key: const Key('z.clone.p')),
        parent: const Key('z'),
        mode: InsertMode.prepend);

    await Future.delayed(duration);
    print('============= remove node "b"! =================');
    _treeViewController.removeNode(const Key('b'));
  }

  [@override](/user/override)
  void dispose() {
    _treeViewController.dispose();
    _controller.dispose();
    super.dispose();
  }

  void _handleRightClick(_, details) {
    print('onNodeSecondaryTapUp!');
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Tab'),
        bottom: TabBar(
          controller: _controller,
          tabs: const [
            Tab(icon: Icon(Icons.laptop_mac)),
            Tab(icon: Icon(Icons.desktop_mac)),
          ],
        ),
      ),
      body: TabBarView(
        controller: _controller,
        children: [
          Row(
            children: [
              Expanded(
                child: Container(
                  decoration: const BoxDecoration(color: Colors.blue),
                  child: TreeView<String>(
                    controller: _treeViewController,
                    onNodeTap: (key) {
                      print('onTap 1 => $key');
                    },
                    onNodeSecondaryTapUp: _handleRightClick,
                  ),
                ),
              ),
              Expanded(
                child: Container(
                  decoration: const BoxDecoration(color: Colors.green),
                  child: TreeView<String>(
                    controller: _treeViewController,
                    onNodeSecondaryTapUp: _handleRightClick,
                  ),
                ),
              ),
            ],
          ),
          Center(
            child: Column(children: [
              TextField(controller: _textEditingController),
              const Text('desktop'),
            ]),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          if (_treeViewController.selectionMode == SelectionMode.single) {
            _treeViewController.selectionMode = SelectionMode.multiple;
          } else {
            _treeViewController.selectionMode = SelectionMode.single;
          }
        },
        child: Text('Toggle mode (${_treeViewController.selectionMode})'),
      ),
    );
  }
}
1 回复

更多关于Flutter macOS树形视图插件macos_tree_view的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


macos_tree_view 是一个用于在 Flutter macOS 应用中显示树形视图的插件。它允许你创建一个层次结构的 UI,类似于 macOS 中的 Finder 或 Xcode 的项目导航器。以下是如何使用 macos_tree_view 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 macos_tree_view 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  macos_tree_view: ^0.1.0  # 请检查最新版本

然后运行 flutter pub get 来获取依赖。

2. 导入包

在你的 Dart 文件中导入 macos_tree_view 包:

import 'package:macos_tree_view/macos_tree_view.dart';

3. 创建树形视图

macos_tree_view 的核心是 TreeView 组件。你需要提供一个 TreeController 来控制树形视图的行为,并提供一个 TreeNode 的层次结构来表示树形数据。

创建一个简单的树形结构

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    // 创建树形结构
    final treeController = TreeController(
      nodes: [
        TreeNode(
          data: 'Root',
          children: [
            TreeNode(data: 'Child 1'),
            TreeNode(data: 'Child 2'),
            TreeNode(
              data: 'Child 3',
              children: [
                TreeNode(data: 'Grandchild 1'),
                TreeNode(data: 'Grandchild 2'),
              ],
            ),
          ],
        ),
      ],
    );

    return MacosWindow(
      child: TreeView(
        controller: treeController,
        nodeBuilder: (BuildContext context, TreeNode node) {
          return Text(node.data);
        },
      ),
    );
  }
}

4. 自定义节点内容

你可以通过 nodeBuilder 回调来自定义每个节点的显示内容。例如,你可以为每个节点添加图标或其他小部件。

TreeView(
  controller: treeController,
  nodeBuilder: (BuildContext context, TreeNode node) {
    return Row(
      children: [
        Icon(Icons.folder),  // 添加图标
        SizedBox(width: 8),
        Text(node.data),
      ],
    );
  },
)

5. 处理节点点击事件

你可以通过 onNodeTap 回调来处理节点的点击事件。

TreeView(
  controller: treeController,
  nodeBuilder: (BuildContext context, TreeNode node) {
    return Text(node.data);
  },
  onNodeTap: (TreeNode node) {
    print('Node tapped: ${node.data}');
  },
)

6. 动态更新树形结构

你可以通过 TreeController 动态添加、删除或更新树形结构中的节点。

// 添加节点
treeController.addNode(TreeNode(data: 'New Child'), parent: treeController.nodes[0]);

// 删除节点
treeController.removeNode(treeController.nodes[0].children[0]);

// 更新节点
treeController.nodes[0].data = 'Updated Root';
treeController.refresh();

7. 完整示例

以下是一个完整的示例,展示了如何使用 macos_tree_view 创建一个简单的树形视图:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final treeController = TreeController(
      nodes: [
        TreeNode(
          data: 'Root',
          children: [
            TreeNode(data: 'Child 1'),
            TreeNode(data: 'Child 2'),
            TreeNode(
              data: 'Child 3',
              children: [
                TreeNode(data: 'Grandchild 1'),
                TreeNode(data: 'Grandchild 2'),
              ],
            ),
          ],
        ),
      ],
    );

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('TreeView Example')),
        body: TreeView(
          controller: treeController,
          nodeBuilder: (BuildContext context, TreeNode node) {
            return Row(
              children: [
                Icon(Icons.folder),
                SizedBox(width: 8),
                Text(node.data),
              ],
            );
          },
          onNodeTap: (TreeNode node) {
            print('Node tapped: ${node.data}');
          },
        ),
      ),
    );
  }
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!