如何实现带全选/取消全选功能的三态CheckBox树形视图(TreeView)
实现n级带CheckBox的树形视图思路
1. 定义递归数据模型
先构建支持嵌套的节点模型,包含状态控制字段:
class TreeNode { final String id; final String name; bool isChecked; bool isIndeterminate; // 半选状态标记 bool isExpanded; List<TreeNode> children; TreeNode({ required this.id, required this.name, this.isChecked = false, this.isIndeterminate = false, this.isExpanded = false, this.children = const [], }); }
2. 实现CheckBox联动逻辑
核心处理选中状态的递归联动:
- 父节点选中/取消:递归同步所有子节点的选中状态,清除半选标记
- 子节点状态变化:向上遍历更新父节点状态:
- 所有子节点全选中 → 父节点设为选中,清除半选
- 所有子节点全未选中 → 父节点设为未选中,清除半选
- 部分子节点选中 → 父节点设为半选
示例逻辑方法:
// 递归更新子节点状态 void syncChildStatus(TreeNode node, bool isChecked) { node.isChecked = isChecked; node.isIndeterminate = false; for (var child in node.children) { syncChildStatus(child, isChecked); } } // 向上更新父节点状态 void updateParentStatus(TreeNode node, List<TreeNode> allNodes) { TreeNode? parent = findParentNode(node.id, allNodes); // 需要实现根据id查找父节点的逻辑 if (parent == null) return; bool allChildChecked = parent.children.every((child) => child.isChecked && !child.isIndeterminate); bool allChildUnchecked = parent.children.every((child) => !child.isChecked && !child.isIndeterminate); if (allChildChecked) { parent.isChecked = true; parent.isIndeterminate = false; } else if (allChildUnchecked) { parent.isChecked = false; parent.isIndeterminate = false; } else { parent.isIndeterminate = true; parent.isChecked = false; } // 继续更新上层父节点 updateParentStatus(parent, allNodes); }
3. 构建递归渲染Widget
通过递归生成节点视图,根据层级设置缩进,处理展开/折叠:
Widget buildTreeNode(TreeNode node, int level, Function() onRefresh) { return Column( children: [ Padding( padding: EdgeInsets.only(left: level * 24.0), // 按层级设置缩进 child: Row( children: [ Checkbox( tristate: true, value: node.isIndeterminate ? null : node.isChecked, onChanged: (bool? value) { if (value != null) { syncChildStatus(node, value); updateParentStatus(node, allTreeNodes); // allTreeNodes为全量节点列表 onRefresh(); } }, ), Expanded(child: Text(node.name)), if (node.children.isNotEmpty) IconButton( icon: Icon(node.isExpanded ? Icons.expand_less : Icons.expand_more), onPressed: () { node.isExpanded = !node.isExpanded; onRefresh(); }, ), ], ), ), // 展开时渲染子节点 if (node.isExpanded) ...node.children.map((child) => buildTreeNode(child, level + 1, onRefresh)).toList(), ], ); }
4. 状态管理
用StatefulWidget直接管理全量节点状态,节点状态变化时调用setState触发视图刷新;也可以用Riverpod、Provider等状态库统一管理,适合复杂场景。
关键细节
- 开启
Checkbox的tristate=true属性,实现半选状态展示 - 通过层级计算缩进,保证树形结构的视觉层级
- 递归逻辑必须覆盖所有层级,避免状态同步遗漏
内容的提问来源于stack exchange,提问作者Akash
相关产品推荐
相关产品推荐

