You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 14:36:15