Flutter横向树形选择器实现效果及完整代码
基于Flutter实现横向树形选择器,核心维护面包屑路径与当前选项列表。点击子项时更新路径,点击面包屑可回退。支持递归查找回显路径和搜索叶子节点。提供泛型抽象类,支持安全区适配与两种交互模式,代码仅700余行。
最近项目里需要实现一个多级分类选择器,类似淘宝那种“电子产品 > 手机 > iPhone”的横向展开效果。找了一圈没看到现成的轮子,索性自己动手做一个。

先看效果
大概就是动图里展示的样子:点击一级项会展开下一级,顶部有面包屑导航可以回退,还支持搜索功能。
为什么要横向?
说实话,一开始想用纵向折叠的方案,但产品觉得层级多了以后界面太乱,用户也不清楚自己选到了第几层。后来调研了几款竞品,发现横向展开加面包屑的设计确实清晰很多——用户一眼就能看到当前路径,操作起来心里有数。
核心思路
整个组件的核心是维护两个状态:
_breadcrumbs— 面包屑路径,记录用户点击过的父级节点_currentOptions— 当前显示的选项列表
每次点击一个有子项的选项,就把这个选项加入面包屑,同时将它的 children 设为当前选项列表;点击面包屑则反向操作,截取数组即可。
/// 导航到子项
void _na vigateToChildren(T parent) {
setState(() {
_breadcrumbs.add(parent);
_currentOptions = parent.children?.map((child) => child as T).toList() ?? [];
});
}
/// 导航到面包屑指定位置
void _na vigateToBreadcrumb(int index) {
setState(() {
_breadcrumbs = _breadcrumbs.sublist(0, index + 1);
_currentOptions = _breadcrumbs[index].children?.map((child) => child as T).toList() ?? [];
});
}
回显这个坑
最麻烦的反而是回显——用户之前选过的值需要重新展示。这就要求在整个树里递归查找,找到目标后把完整路径构建出来:
/// 递归查找项目路径 bool _findItemPath(Listoptions, T target, List path) { for (var option in options) { path.add(option); if (option.value == target.value) { return true; } if (option.children != null && option.children!.isNotEmpty) { List children = option.children!.map((child) => child as T).toList(); if (_findItemPath(children, target, path)) { return true; } } path.removeLast(); // 回溯 } return false; }
这个回溯算法挺经典的:找到了就一路返回 true,没找到就把刚加的节点删掉,继续尝试下一个。
搜索功能
搜索是后来加的,需求很明确:只搜索叶子节点(最终可选的项),然后显示完整路径,让用户知道这个选项在哪个分类下。
void _searchInTree(Listoptions, String query, List results, List path) { for (var option in options) { final bool isLeaf = option.children == null || option.children!.isEmpty; final bool nameMatches = option.name?.toLowerCase().contains(query) == true; if (isLeaf && nameMatches) { results.add(option); } // 递归搜索子项 if (option.children != null && option.children!.isNotEmpty) { List newPath = List.from(path)..add(option); List children = option.children!.map((child) => child as T).toList(); _searchInTree(children, query, results, newPath); } } }
数据结构设计
为了通用性,定义了一个抽象类,业务方继承一下就能用:
abstract class OptionChildrenItem {
String? name;
String? value;
List? children;
OptionChildrenItem({this.name, this.value, this.children});
}
同时还提供了一个默认实现 OptionChildrenItemImpl,不想自定义的话可以直接用。
使用方式
调用起来很简单,一行代码就能弹出来:
final result = await showHorizontalTreePopup(
context: context,
options: _demoOptions,
title: '请选择分类',
themeColor: Colors.blue,
showSearch: true, // 是否显示搜索框
showConfirmButton: true, // 是否显示确定按钮;false 则选中后直接返回
onConfirm: (selectedItem) {
print('选中了: ${selectedItem?.name}');
},
);
一些细节处理
底部安全区 — iPhone 的小横条要留出来,用
MediaQuery.of(context).padding.bottom处理。泛型支持 — 整个组件是泛型的,
T extends OptionChildrenItem,这样业务方自己的数据模型也能直接使用。两种交互模式 —
showConfirmButton为 true 时需要点击确定才返回,为 false 时点击叶子节点直接返回,适应不同场景。
总结
整体下来代码量不大,700 多行。核心逻辑就是维护面包屑和当前列表两个状态。最折腾的地方是泛型转换——Dart 的泛型协变有点坑,children 那里需要手动 map 转一下类型。
有需要的可以直接用,pub.dev 搜 horizontal_tree_selector 就行:
dependencies: horizontal_tree_selector: ^0.0.1
Adobe Acrobat Pro 是面向办公、法务、财务、教育及出版用户的专业PDF处理软件,可在Mac上创建、编辑、转换、合并和保护文档,并提供OCR文字识别、表单、批注、内容比较、敏感信息遮盖及电子签名功能。
Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。
Shapr3D是一款面向工业设计、机械工程、建筑概念和三维打印工作流的CAD软件。Mac版采用Parasolid建模内核,支持草图约束、实体建模、工程图、可视化渲染及常见CAD格式交换,并可通过账户在多台设备之间同步项目。
AutoCAD 是 Autodesk 推出的专业计算机辅助设计软件,Windows 版支持精确二维绘图、三维实体与曲面建模、图纸标注、布局发布和任务自动化,并包含多套行业专业化工具组合,适用于建筑、机械、电气、工程建设及制造设计人员。
Adobe Acrobat Pro 是面向专业 PDF 工作的综合解决方案,可在 Windows 中创建、编辑、转换、比较、整理和保护文档,并支持表单、电子签名、扫描件文字识别及敏感信息永久遮盖。订阅还包含网页、移动服务和云端协作能力,
Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。











