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

如何在Flutter中用ExpansionTile实现州与城市层级选择(附JSON数据)

实现Flutter州-城市层级选择界面(基于ExpansionTile)

步骤1:定义数据模型

先把JSON数据转换成Dart实体类,方便后续操作。假设你的JSON结构如下:

{
  "states": [
    {
      "name": "California",
      "cities": ["Los Angeles", "San Francisco", "San Diego"]
    },
    {
      "name": "Texas",
      "cities": ["Houston", "Dallas", "Austin"]
    }
  ]
}

对应的Dart模型:

class StateModel {
  final String name;
  final List<String> cities;

  StateModel({required this.name, required this.cities});

  // 从JSON转换
  factory StateModel.fromJson(Map<String, dynamic> json) {
    return StateModel(
      name: json['name'],
      cities: List<String>.from(json['cities']),
    );
  }
}

步骤2:解析JSON数据

加载并解析你的JSON(以本地asset为例,网络请求逻辑可直接替换):

// 假设JSON文件在assets/us_states.json
Future<List<StateModel>> loadStates() async {
  final String response = await rootBundle.loadString('assets/us_states.json');
  final data = await json.decode(response);
  return List<StateModel>.from(data['states'].map((x) => StateModel.fromJson(x)));
}

步骤3:构建层级选择UI

使用ExpansionTile作为州的可展开容器,内部嵌套城市列表,同时维护选中状态:

class StateCitySelector extends StatefulWidget {
  const StateCitySelector({super.key});

  @override
  State<StateCitySelector> createState() => _StateCitySelectorState();
}

class _StateCitySelectorState extends State<StateCitySelector> {
  List<StateModel>? _states;
  String? _selectedState;
  String? _selectedCity;

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    _states = await loadStates();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    if (_states == null) {
      return const Center(child: CircularProgressIndicator());
    }

    return Scaffold(
      appBar: AppBar(title: const Text('州-城市选择器')),
      body: ListView.builder(
        itemCount: _states!.length,
        itemBuilder: (context, index) {
          final state = _states![index];
          return ExpansionTile(
            title: Text(
              state.name,
              style: TextStyle(
                fontWeight: _selectedState == state.name ? FontWeight.bold : FontWeight.normal,
                color: _selectedState == state.name ? Colors.blue : null,
              ),
            ),
            // 展开时显示城市列表
            children: state.cities.map((city) {
              return ListTile(
                title: Text(
                  city,
                  style: TextStyle(
                    fontWeight: _selectedCity == city ? FontWeight.bold : FontWeight.normal,
                    color: _selectedCity == city ? Colors.green : null,
                  ),
                ),
                onTap: () {
                  setState(() {
                    _selectedState = state.name;
                    _selectedCity = city;
                  });
                  // 这里可以添加选择后的逻辑,比如返回结果或更新父组件
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('已选择:${state.name} - $city')),
                  );
                },
              );
            }).toList(),
          );
        },
      ),
    );
  }
}

关键说明

  • ExpansionTile:自动处理展开/收起动画,title显示州名,children是对应城市的ListTile
  • 选中状态:通过_selectedState和_selectedCity记录选中项,更新UI样式(加粗、变色)
  • 数据加载:用Future异步加载JSON,加载完成前显示加载指示器

注意事项

  1. 确保在pubspec.yaml中声明本地JSON asset:
assets:
  - assets/us_states.json
  1. 如果是网络JSON数据,替换loadStates中的rootBundle为网络请求(比如http.get)
  2. 可根据需求扩展功能,比如添加搜索框、多选支持等

内容的提问来源于stack exchange,提问作者Bhismydv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 22:10:29