Flutter如何实现嵌套依赖动态数据的级联下拉选择功能
Flutter 无限层级级联下拉实现方案
核心实现逻辑非常简单,不需要写递归组件,只用维护两个状态列表就能适配任意深度的嵌套结构:
_levelOptions:按层级顺序存储每一级下拉的选项数据源,索引0对应第一级下拉的选项,索引1对应第二级,以此类推_selectedValues:按层级顺序存储每一级下拉的选中值,和_levelOptions索引一一对应
选中某层级选项时,自动清空该层级之后的所有旧选项和选中值,再判断当前选中项是否有子节点,有就追加下一级的选项源,自动触发UI重建渲染新的下拉框。
具体实现步骤
1. 新增页面状态变量
修改_HomePageState的代码,加入状态定义和初始化:
class _HomePageState extends State<HomePage> { // 每一级下拉的选项集合 late List<List<LocationDetail>> _levelOptions; // 每一级下拉的选中值 late List<LocationDetail?> _selectedValues; @override void initState() { super.initState(); _levelOptions = []; _selectedValues = []; } // 保留原有的_getJsonData方法不变 Future<List<LocationDetail>> _getJsonData() async { final contents = await rootBundle.loadString( 'assets/location.json', ); List<dynamic> dataList = jsonDecode(contents); List<LocationDetail> locationList = dataList.map((v) => LocationDetail.fromMap(v)).toList(); return locationList; }
2. 编写选中事件通用处理逻辑
这个方法是核心,统一处理所有层级下拉的选中逻辑,自动维护层级状态:
/// 处理指定层级的选项选中 void _onSelectLevel(int level, LocationDetail? selectedItem) { if (selectedItem == null) return; setState(() { // 更新当前层级的选中值 _selectedValues[level] = selectedItem; // 清空当前层级之后的所有旧选项、旧选中值(比如重新选了第一级,后面二三级都要删掉重置) _levelOptions.removeRange(level + 1, _levelOptions.length); _selectedValues.removeRange(level + 1, _selectedValues.length); // 当前选中项有子节点的话,追加下一级的选项 if (selectedItem.childrens != null && selectedItem.childrens!.isNotEmpty) { _levelOptions.add(selectedItem.childrens!); _selectedValues.add(null); } }); }
3. 重构FutureBuilder成功分支的UI渲染
拿到顶层数据后初始化第一级选项,然后遍历_levelOptions动态生成对应数量的下拉框:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('级联选择示例')), body: FutureBuilder<List<LocationDetail>>( future: _getJsonData(), builder: (context, snapshot) { if (snapshot.hasData) { // 首次加载完成时初始化第一级选项 if (_levelOptions.isEmpty) { _levelOptions = [snapshot.data!]; _selectedValues = [null]; } // 横向排列所有下拉框,支持横向滚动避免溢出 return SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.all(16), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: List.generate(_levelOptions.length, (levelIndex) { return Container( width: 180, margin: const EdgeInsets.only(right: 16), child: DropdownButton<LocationDetail>( value: _selectedValues[levelIndex], isExpanded: true, hint: Text('选择第${levelIndex + 1}级'), items: _levelOptions[levelIndex].map((item) { return DropdownMenuItem( value: item, child: Text( item.name, overflow: TextOverflow.ellipsis, ), ); }).toList(), onChanged: (val) => _onSelectLevel(levelIndex, val), ), ); }), ), ); } else if (snapshot.hasError) { return Center(child: Text(snapshot.error.toString())); } else { return const Center(child: CircularProgressIndicator()); } }, ), ); } }
效果说明
- 初始加载完成后,
_levelOptions长度为1,仅渲染第一级下拉,选项为顶层的NewYork - 选中NewYork后,检测到存在子节点,自动追加第二级选项,渲染第二个下拉,选项为Rental Car 1、Rental Car 2
- 选中Rental Car 1时,检测到没有子节点,不会追加新下拉,停留在2个下拉的状态
- 选中Rental Car 2时,检测到存在子节点,自动追加第三级选项,渲染第三个下拉,选项为Rental Car 21、Rental Car 22
- 无论JSON嵌套多少层,逻辑都能自动适配,不需要手动修改层级相关代码
小提示:如果需要获取最终选中的末级节点,直接取
_selectedValues.last即可;如果需要获取完整选择路径,_selectedValues里按顺序存储的所有非空值就是从顶层到末级的完整节点链。
内容的提问来源于stack exchange,提问作者KonTash
相关产品推荐
相关产品推荐

