Flutter可检查树视图插件checkable_treeview的使用

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

Flutter可检查树视图插件checkable_treeview的使用

Checkable TreeView

Checkable TreeView 是一个用于 Flutter 的可选中和可定制的树形视图小部件。

Screenshot

示例截图

Features

  • 层次数据展示
  • 节点选择支持多选
  • 节点可展开/折叠
  • 过滤和排序功能
  • 自定义节点外观
  • “全选”功能
  • 全部展开/折叠节点选项

Getting Started

要在您的 Flutter 项目中使用 TreeView 小部件,请按照以下步骤操作:

flutter pub add checkable_treeview

Usage

以下是使用 TreeView 小部件的基本示例:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('TreeView Example'),
        ),
        body: TreeView<String>(
          nodes: [
            TreeNode(
              label: const Text('Root'), // 根节点标签
              value: 'root', // 根节点值
              icon: Icon(Icons.folder), // 根节点图标
              children: [
                TreeNode(label: const Text('Child 1'), value: 'child1'), // 子节点1
                TreeNode(label: const Text('Child 2'), value: 'child2'), // 子节点2
              ],
            ),
          ],
          onSelectionChanged: (selectedValues) { // 当节点选择变化时触发
            print('Selected values: $selectedValues');
          },
        ),
      ),
    );
  }
}

Customization

TreeView 小部件提供了各种自定义选项:

  • showSelectAll: 启用/禁用“全选”复选框
  • selectAllWidget: 自定义“全选”选项的小部件
  • showExpandCollapseButton: 显示/隐藏展开/折叠按钮
  • initialExpandedLevels: 设置初始展开级别数

有关更高级的自定义,请参阅 API 文档。

Advanced Features

Filtering

要实现过滤,使用 TreeViewStatefilter 方法:

final treeViewKey = GlobalKey<TreeViewState<String>>();

treeViewKey.currentState?.filter('search keyword');
Sorting

要实现排序,使用 TreeViewStatesort 方法:

final treeViewKey = GlobalKey<TreeViewState<String>>();

treeViewKey.currentState?.sort((a, b) => a.label.compareTo(b.label));
Set Select All

要设置全选状态,使用 TreeViewStatesetSelectAll 方法:

final treeViewKey = GlobalKey<TreeViewState<String>>();

treeViewKey.currentState?.setSelectAll(true);
Expand/Collapse All

要展开或折叠所有节点,使用 TreeViewStateexpandAllcollapseAll 方法:

final treeViewKey = GlobalKey<TreeViewState<String>>();

treeViewKey.currentState?.expandAll();
treeViewKey.currentState?.collapseAll();
Get Selected Nodes

要获取选定的节点,使用 TreeViewStategetSelectedNodes 方法:

final treeViewKey = GlobalKey<TreeViewState<String>>();

final selectedNodes = treeViewKey.currentState?.getSelectedNodes();
Get Selected Values

要获取选定的值,使用 TreeViewStategetSelectedValues 方法:

final treeViewKey = GlobalKey<TreeViewState<String>>();

final selectedValues = treeViewKey.currentState?.getSelectedValues();

完整示例 Demo

下面是一个完整的示例,包含搜索、排序、全选等功能:

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

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

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

enum SortOrder { defaultOrder, ascending, descending }

class _MyHomePageState extends State<MyHomePage> {
  List<TreeNode<String>> _nodes = [];
  String _searchKeyword = '';
  final TextEditingController _searchController = TextEditingController();
  final _treeViewKey = GlobalKey<TreeViewState<String>>();
  SortOrder _currentSortOrder = SortOrder.defaultOrder;

  [@override](/user/override)
  void initState() {
    super.initState();
    _nodes = [
      TreeNode(
        label: const Text('Project Folder'),
        value: 'project_folder',
        trailing: (context, node) {
          return Text(
              '(${_treeViewKey.currentState?.getChildSelectedValues(node).length} selected)');
        },
        children: [
          TreeNode(
            label: const Text('src'),
            icon: Icon(Icons.folder_open),
            children: [
              TreeNode(
                  label: const Text('main.js'),
                  value: 'main_js',
                  icon: Icon(Icons.javascript),
                  isSelected: true),
              TreeNode(
                  label: const Text('app.js'),
                  value: 'app_js',
                  icon: Icon(Icons.javascript)),
              TreeNode(
                  label: const Text('styles.css'),
                  value: 'styles_css',
                  icon: Icon(Icons.css)),
            ],
          ),
          TreeNode(
            label: const Text('public'),
            value: 'public_folder',
            icon: Icon(Icons.folder_open),
            children: [
              TreeNode(
                  label: const Text('index.html'),
                  value: 'index_html',
                  icon: Icon(Icons.html)),
              TreeNode(
                  label: const Text('favicon.ico'),
                  value: 'favicon',
                  icon: Icon(Icons.image)),
            ],
          ),
        ],
      ),
      TreeNode(
        label: const Text('Config Files'),
        value: 'config_folder',
        children: [
          TreeNode(
              label: const Text('package.json'),
              value: 'package_json',
              icon: Icon(Icons.settings)),
          TreeNode(
            label: const Text('.gitignore'),
            value: 'gitignore',
            icon: Icon(Icons.remove_red_eye),
            trailing: (context, node) {
              return Text(node.data as String);
            },
            data: '1 KB',
          ),
        ],
      ),
    ];
  }

  void _onSelectionChanged(List<String?> selectedValues) {
    print('Selected node values: $selectedValues');
  }

  bool _filterNode(TreeNode<String> node) {
    if (_searchKeyword.isEmpty) {
      return true;
    }
    return node.value?.toLowerCase().contains(_searchKeyword.toLowerCase()) ??
        false;
  }

  void _performSearch() {
    setState(() {
      _searchKeyword = _searchController.text;
      _treeViewKey.currentState?.filter(_filterNode);
    });
    _printSelectedNodes();
  }

  void _sortNodes(SortOrder order) {
    setState(() {
      _currentSortOrder = order;
      switch (order) {
        case SortOrder.defaultOrder:
          _treeViewKey.currentState?.sort(null);
          break;
        case SortOrder.ascending:
          _treeViewKey.currentState
              ?.sort((a, b) => (a.value ?? '').compareTo(b.value ?? ''));
          break;
        case SortOrder.descending:
          _treeViewKey.currentState
              ?.sort((a, b) => (b.value ?? '').compareTo(a.value ?? ''));
          break;
      }
    });
  }

  void _expandAll() {
    _treeViewKey.currentState?.expandAll();
  }

  void _collapseAll() {
    _treeViewKey.currentState?.collapseAll();
  }

  void _printSelectedNodes() {
    List<TreeNode<String>> selectedNodes =
        _treeViewKey.currentState?.getSelectedNodes() ?? [];
    print('Selected nodes:');
    for (var node in selectedNodes) {
      print('Value: ${node.value}, Label: ${node.label}');
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _searchController,
                    decoration: const InputDecoration(
                      hintText: 'Enter search keyword',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                ElevatedButton(
                  onPressed: _performSearch,
                  child: const Text('Search'),
                ),
                const SizedBox(width: 8),
                SizedBox(
                  width: 150, // 设置固定宽度
                  child: DropdownButton<SortOrder>(
                    isExpanded: true,
                    value: _currentSortOrder,
                    onChanged: (SortOrder? newValue) {
                      if (newValue != null) {
                        _sortNodes(newValue);
                      }
                    },
                    items: SortOrder.values.map((SortOrder order) {
                      return DropdownMenuItem<SortOrder>(
                        value: order,
                        child: Text(_getSortOrderText(order)),
                      );
                    }).toList(),
                  ),
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                ElevatedButton(
                  onPressed: _expandAll,
                  child: const Text('Expand All'),
                ),
                ElevatedButton(
                  onPressed: _collapseAll,
                  child: const Text('Collapse All'),
                ),
                ElevatedButton(
                  onPressed: () {
                    _treeViewKey.currentState?.setSelectAll(true);
                  },
                  child: const Text('全部选中'),
                ),
                ElevatedButton(
                  onPressed: () {
                    _treeViewKey.currentState?.setSelectAll(false);
                  },
                  child: const Text('Deselect All'),
                ),
                ElevatedButton(
                  onPressed: _printSelectedNodes,
                  child: const Text('Print Selected Nodes'),
                ),
              ],
            ),
          ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: TreeView<String>(
                key: _treeViewKey,
                nodes: _nodes,
                onSelectionChanged: _onSelectionChanged,
                initialExpandedLevels: 1,
                showSelectAll: true,
                selectAllWidget: const Text('Select All'),
                selectAllTrailing: (context) {
                  return Text(
                      '(${_treeViewKey.currentState?.getSelectedNodes().length} selected)');
                },
                showExpandCollapseButton: true,
              ),
            ),
          ),
        ],
      ),
    );
  }

  String _getSortOrderText(SortOrder order) {
    switch (order) {
      case SortOrder.defaultOrder:
        return 'Default Order';
      case SortOrder.ascending:
        return 'Ascending';
      case SortOrder.descending:
        return 'Descending';
    }
  }

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

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

1 回复

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


当然,以下是如何在Flutter中使用checkable_treeview插件来创建一个可检查的树视图的示例代码。checkable_treeview是一个Flutter包,用于显示可检查的树结构。

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

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

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

接下来,在你的Flutter项目中,你可以按照以下方式使用checkable_treeview

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Checkable Treeview Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Checkable Treeview Demo'),
        ),
        body: CheckableTreeViewDemo(),
      ),
    );
  }
}

class CheckableTreeViewDemo extends StatefulWidget {
  @override
  _CheckableTreeViewDemoState createState() => _CheckableTreeViewDemoState();
}

class _CheckableTreeViewDemoState extends State<CheckableTreeViewDemo> {
  final List<CheckableNode> nodes = [
    CheckableNode(
      title: 'Parent 1',
      children: [
        CheckableNode(title: 'Child 1.1'),
        CheckableNode(title: 'Child 1.2'),
      ],
    ),
    CheckableNode(
      title: 'Parent 2',
      children: [
        CheckableNode(
          title: 'Child 2.1',
          children: [
            CheckableNode(title: 'Grandchild 2.1.1'),
            CheckableNode(title: 'Grandchild 2.1.2'),
          ],
        ),
        CheckableNode(title: 'Child 2.2'),
      ],
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: CheckableTreeView(
        nodes: nodes,
        onChanged: (List<CheckableNode> checkedNodes) {
          // 处理选中的节点
          print('Checked nodes: $checkedNodes');
        },
      ),
    );
  }
}

在这个示例中:

  1. 我们定义了一个包含树节点的列表nodes,其中每个节点都是一个CheckableNode对象。
  2. CheckableNode对象包含标题(title)和子节点列表(children)。
  3. CheckableTreeViewDemo组件中,我们使用CheckableTreeView小部件来显示树视图,并传入节点列表。
  4. 当树中的节点被选中或取消选中时,onChanged回调会被触发,并传递当前选中的节点列表。

这个示例展示了如何使用checkable_treeview包来创建一个简单的可检查树视图。你可以根据需要进一步自定义和扩展这个示例。

回到顶部