如何在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,加载完成前显示加载指示器
注意事项
- 确保在
pubspec.yaml中声明本地JSON asset:
assets: - assets/us_states.json
- 如果是网络JSON数据,替换
loadStates中的rootBundle为网络请求(比如http.get) - 可根据需求扩展功能,比如添加搜索框、多选支持等
内容的提问来源于stack exchange,提问作者Bhismydv
相关产品推荐
相关产品推荐

