import 'package:flutter/material.dart'; import '../data/region_data.dart'; /// 省→市→区三级联动结果。 class RegionResult { const RegionResult({ required this.provinceCode, required this.provinceName, required this.cityCode, required this.cityName, required this.districtCode, required this.districtName, }); final String provinceCode; final String provinceName; final String cityCode; final String cityName; final String districtCode; final String districtName; /// 拼接展示文本(省名+市名+区名)。 String get label => '$provinceName$cityName$districtName'; } /// 省→市→区三级联动底部弹起选择器。 /// /// 数据源统一来自 [RegionData](已加载的 `province.json`)。 /// 返回 [RegionResult](含 code 与 name);用户取消时弹出 null。 class RegionPicker extends StatefulWidget { const RegionPicker({ super.key, this.initialProvinceCode, this.initialCityCode, this.initialDistrictCode, }); final String? initialProvinceCode; final String? initialCityCode; final String? initialDistrictCode; /// 打开选择器底部弹窗,返回所选 [RegionResult] 或 null。 static Future show({ required BuildContext context, String? initialProvinceCode, String? initialCityCode, String? initialDistrictCode, }) { return showModalBottomSheet( useSafeArea: true, context: context, isScrollControlled: true, builder: (ctx) => RegionPicker( initialProvinceCode: initialProvinceCode, initialCityCode: initialCityCode, initialDistrictCode: initialDistrictCode, ), ); } @override State createState() => _RegionPickerState(); } class _RegionPickerState extends State { final RegionData _region = RegionData.instance; /// 当前选中层级:0=省 1=市 2=区。 int _level = 0; String? _provinceCode; String? _cityCode; String? _districtCode; @override void initState() { super.initState(); _provinceCode = widget.initialProvinceCode; _cityCode = widget.initialCityCode; _districtCode = widget.initialDistrictCode; // 若已带初始省,直接进入市层级以便快速定位。 if (_provinceCode != null && _provinceCode!.isNotEmpty) { _level = 1; if (_cityCode != null && _cityCode!.isNotEmpty) { _level = 2; } } } List get _currentList { switch (_level) { case 0: return _region.provinces; case 1: return _region.citiesOf(_provinceCode ?? ''); case 2: return _region.districtsOf( _provinceCode ?? '', _cityCode ?? '', ); default: return const []; } } String _levelTitle() { switch (_level) { case 0: return '选择省份'; case 1: return '选择城市'; case 2: return '选择区县'; default: return '选择地区'; } } void _onSelect(RegionNode node) { if (_level == 0) { setState(() { _provinceCode = node.code; _cityCode = null; _districtCode = null; _level = 1; }); } else if (_level == 1) { setState(() { _cityCode = node.code; _districtCode = null; _level = 2; }); } else { final provinceName = _region.nameOf(_provinceCode ?? ''); final cityName = _region.nameOf(_cityCode ?? ''); Navigator.pop( context, RegionResult( provinceCode: _provinceCode ?? '', provinceName: provinceName, cityCode: _cityCode ?? '', cityName: cityName, districtCode: node.code, districtName: node.name, ), ); } } @override Widget build(BuildContext context) { final nodes = _currentList; return Container( height: MediaQuery.of(context).size.height * 0.6, padding: const EdgeInsets.only(top: 8), child: Column( children: [ Row( children: [ const SizedBox(width: 16), Expanded( child: Text( _levelTitle(), style: Theme.of(context) .textTheme .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), ), if (_level > 0) TextButton( onPressed: () => setState(() => _level--), child: const Text('上一级'), ), const SizedBox(width: 8), ], ), const Divider(height: 1), Expanded( child: ListView.separated( itemCount: nodes.length, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (context, index) { final node = nodes[index]; final isSelected = _isSelected(node); return ListTile( title: Text(node.name), trailing: isSelected ? Icon(Icons.check, color: Theme.of(context).colorScheme.primary) : const Icon(Icons.chevron_right), onTap: () => _onSelect(node), ); }, ), ), ], ), ); } bool _isSelected(RegionNode node) { if (_level == 0) return node.code == _provinceCode; if (_level == 1) return node.code == _cityCode; return node.code == _districtCode; } }