import 'dart:convert'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:vbvs_app/common/pojo/city.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/ListSearchWidget.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/controller/person/person_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/language/AppLanguage.dart'; import 'package:vbvs_app/pages/common/selectDialog.dart'; CityModelController cityModelController = Get.put(CityModelController()); // 修改这个方法,返回 List Future> loadCityData() async { try { // 获取当前语言代码 String currentLanguageCode = AppLanguage().getCurrentLanguageCode(); // 根据当前语言代码构建文件名 final String fileName = 'assets/city/city_$currentLanguageCode.json'; // 读取对应语言的JSON文件 final String jsonString = await rootBundle.loadString(fileName); // 解析JSON数据并转换为 CityModel 列表 final List jsonList = jsonDecode(jsonString); // 将 Map 转换为 CityModel return jsonList.map((json) => CityModel.fromJson(json)).toList(); } catch (e) { print('Error loading city data: $e'); return []; } } // 定义城市选择器颜色配置 class CitySelectionColors { final Color? pickerBackgroundColor; // 选择器整体背景色 final Color? confirmTextColor; // 确定按钮文字颜色 final Color? selectedCityColor; // 选中的城市背景色 final Color? selectedTextColor; // 选中的文字颜色 const CitySelectionColors({ this.pickerBackgroundColor, this.confirmTextColor, this.selectedCityColor, this.selectedTextColor, }); } Future showCitySelectionDialog( BuildContext context, { required CityModel selectedCity, Function? onCityChanged, String title = "选择城市", required Future> cityDataFuture, // 改为required参数 CitySelectionColors? colors, // 新增:颜色配置参数 }) { ThemeController themeController = Get.find(); final bool isChinese = Get.locale?.languageCode == 'zh_CN' ?? true; // 使用传入的颜色,如果没传则使用主题颜色 final Color pickerBackgroundColor = colors?.pickerBackgroundColor ?? themeController.currentColor.sc17; final Color confirmTextColor = colors?.confirmTextColor ?? themeController.currentColor.sc2; final Color selectedCityColor = colors?.selectedCityColor ?? themeController.currentColor.sc2; final Color selectedTextColor = colors?.selectedTextColor ?? themeController.currentColor.sc1; // 默认使用主题颜色 final RxList countries = [].obs; final RxList provinces = [].obs; final RxList cities = [].obs; final RxInt countryIndex = 0.obs; final RxInt provinceIndex = 0.obs; final RxInt cityIndex = 0.obs; cityModelController.searchResults = []; // 更新城市列表 void updateCities(List cityData) { try { if (provinces.isEmpty || countries.isEmpty) return; final selectedCountry = countries[countryIndex.value]; final selectedProvince = provinces[provinceIndex.value]; // 在层级结构中查找对应的城市 for (var country in cityData) { if (country.value == selectedCountry) { for (var province in country.children ?? []) { if (province.value == selectedProvince) { // 安全地处理城市列表,过滤掉 null 值并转换为 String final List cityList = []; for (var city in province.children ?? []) { final cityName = city?.value ?? city?.city ?? ''; if (cityName.isNotEmpty) { cityList.add(cityName); } } cities.value = cityList; // 设置默认选中城市 if ((selectedCity.value != null && cities.contains(selectedCity.value)) || (selectedCity.city != null && cities.contains(selectedCity.city))) { cityIndex.value = cities .indexOf(selectedCity.value ?? selectedCity.city ?? ''); } else { cityIndex.value = cities.isNotEmpty ? 0 : 0; } return; } } } } cities.value = []; cityIndex.value = 0; } catch (e) { ef.log("获取城市数据失败:$e"); cities.value = []; cityIndex.value = 0; } } void updateProvinces(List cityData) { try { if (countries.isEmpty) return; final selectedCountry = countries[countryIndex.value]; // 在层级结构中查找对应的省份 for (var country in cityData) { if (country.value == selectedCountry) { // 安全地处理省份列表,过滤掉 null 值并转换为 String final List provinceList = []; for (var province in country.children ?? []) { final provinceName = province?.value ?? province?.province ?? ''; if (provinceName.isNotEmpty) { provinceList.add(provinceName); } } provinces.value = provinceList; // 设置默认选中省份 if ((selectedCity.province != null && provinces.contains(selectedCity.province)) || (selectedCity.value != null && provinces.contains(selectedCity.value))) { provinceIndex.value = provinces .indexOf(selectedCity.province ?? selectedCity.value ?? ''); } else { provinceIndex.value = provinces.isNotEmpty ? 0 : 0; } updateCities(cityData); return; } } provinces.value = []; provinceIndex.value = 0; cities.value = []; cityIndex.value = 0; } catch (e) { ef.log("获取省份数据失败:$e"); provinces.value = []; provinceIndex.value = 0; cities.value = []; cityIndex.value = 0; } } void initializePickerData(List cityData) { try { // 安全地处理国家列表,过滤掉 null 值并转换为 String final List countryList = []; for (var country in cityData) { final countryName = country.value ?? country.country ?? ''; if (countryName.isNotEmpty) { countryList.add(countryName); } } countries.value = countryList; // 设置默认选中国家 if ((selectedCity.country != null && countries.contains(selectedCity.country)) || (selectedCity.value != null && countries.contains(selectedCity.value))) { countryIndex.value = countries.indexOf(selectedCity.country ?? selectedCity.value ?? ''); } else { countryIndex.value = countries.isNotEmpty ? 0 : 0; } updateProvinces(cityData); } catch (e) { ef.log("初始化选择器数据失败:$e"); countries.value = []; provinces.value = []; cities.value = []; countryIndex.value = 0; provinceIndex.value = 0; cityIndex.value = 0; } } // 获取选中的完整城市数据 CityModel? getSelectedCityData(List cityData) { if (countries.isEmpty || provinces.isEmpty || cities.isEmpty) return null; final selectedCountry = countries[countryIndex.value]; final selectedProvince = provinces[provinceIndex.value]; final selectedCityName = cities[cityIndex.value]; // 在层级结构中查找对应的城市 for (var country in cityData) { if (country.value == selectedCountry || country.country == selectedCountry) { for (var province in country.children ?? []) { if (province.value == selectedProvince || province.province == selectedProvince) { for (var city in province.children ?? []) { if (city.value == selectedCityName || city.city == selectedCityName) { return city; } } } } } } return null; } return showModalBottomSheet( context: context, backgroundColor: Colors.transparent, isScrollControlled: true, enableDrag: false, isDismissible: true, builder: (BuildContext context) { return Container( color: Colors.transparent, child: Column( mainAxisSize: MainAxisSize.min, children: [ // 半透明遮罩层,点击关闭 Expanded( child: GestureDetector( onTap: () => Navigator.of(context).pop(), child: Container( color: Colors.black.withOpacity(0.5), ), ), ), // 弹窗内容 FutureBuilder>( future: cityDataFuture, builder: (context, snapshot) { // 数据加载中 if (snapshot.connectionState == ConnectionState.waiting) { return _buildLoadingBottomSheet( themeController, pickerBackgroundColor, confirmTextColor, ); } // 数据加载错误 if (snapshot.hasError || !snapshot.hasData) { return _buildErrorBottomSheet( themeController, context, pickerBackgroundColor, confirmTextColor, ); } final cityData = snapshot.data!; // 只在第一次初始化数据 // if (countries.isEmpty) { // } initializePickerData(cityData); return _buildCityPickerContent( context, themeController, isChinese, title, cityData, onCityChanged, pickerBackgroundColor: pickerBackgroundColor, confirmTextColor: confirmTextColor, selectedCityColor: selectedCityColor, selectedTextColor: selectedTextColor, countries: countries, provinces: provinces, cities: cities, countryIndex: countryIndex, provinceIndex: provinceIndex, cityIndex: cityIndex, ); }, ), ], ), ); }, ); } // 根据ID查找完整的城市数据(包含国家、省份、城市信息) CityModel? findCompleteCityDataById(int? id, List cityData) { if (id == null) return null; try { // 遍历三级数据结构查找匹配的ID for (var country in cityData) { // 检查国家节点是否有ID匹配 if (country.id == id) { return CityModel( id: country.id, value: country.value, label: country.label, country: country.value ?? country.country, province: null, city: null, UTC: country.UTC, children: country.children, ); } // 检查省份节点 for (var province in country.children ?? []) { if (province.id == id) { return CityModel( id: province.id, value: province.value, label: province.label, country: country.value ?? country.country, province: province.value ?? province.province, city: null, UTC: province.UTC, children: province.children, ); } // 检查城市节点 for (var city in province.children ?? []) { if (city.id == id) { return CityModel( id: city.id, value: city.value, label: city.label, country: country.value ?? country.country, province: province.value ?? province.province, city: city.value ?? city.city, UTC: city.UTC, children: city.children, ); } } } } return null; } catch (e) { ef.log("根据ID查找完整城市数据失败:$e"); return null; } } // 城市选择器内容 Widget _buildCityPickerContent( BuildContext context, ThemeController themeController, bool isChinese, String title, List cityData, Function? onCityChanged, { required Color pickerBackgroundColor, required Color confirmTextColor, required Color selectedCityColor, required Color selectedTextColor, required RxList countries, required RxList provinces, required RxList cities, required RxInt countryIndex, required RxInt provinceIndex, required RxInt cityIndex, }) { final bottomInsets = MediaQuery.of(context).viewInsets.bottom; // 内部更新方法 void updateCities() { try { if (provinces.isEmpty || countries.isEmpty) return; final selectedCountry = countries[countryIndex.value]; final selectedProvince = provinces[provinceIndex.value]; // 查找匹配的国家和省份 final provinceData = cityData .firstWhere( (country) => country.value == selectedCountry, orElse: () => CityModel(), ) .children ?.firstWhere( (province) => province.value == selectedProvince, orElse: () => CityModel(), ); // 提取城市列表 final cityList = (provinceData?.children ?? []) .map((city) => city?.value ?? city?.city ?? '') .where((cityName) => cityName.toString().isNotEmpty) .toList() .cast(); cities.value = cityList; cityIndex.value = cities.isNotEmpty ? 0 : 0; } catch (e) { ef.log("更新城市列表失败:$e"); cities.value = []; cityIndex.value = 0; } } void updateProvinces() { try { if (countries.isEmpty) return; final selectedCountry = countries[countryIndex.value]; for (var country in cityData) { if (country.value == selectedCountry) { final List provinceList = []; for (var province in country.children ?? []) { final provinceName = province?.value ?? province?.province; if (provinceName != null && provinceName.isNotEmpty) { provinceList.add(provinceName); } } provinces.value = provinceList; provinceIndex.value = provinces.isNotEmpty ? 0 : 0; updateCities(); return; } } provinces.value = []; provinceIndex.value = 0; cities.value = []; cityIndex.value = 0; } catch (e) { ef.log("获取省份数据失败:$e"); provinces.value = []; provinceIndex.value = 0; cities.value = []; cityIndex.value = 0; } } CityModel? getSelectedCityData() { try { if (countries.isEmpty) return null; final selectedCountry = countries[countryIndex.value]; for (var country in cityData) { final countryName = country.value ?? country.country; if (countryName == selectedCountry) { if ((country.children == null || country.children!.isEmpty) && (country.city != null || country.value != null)) { return country; } if (provinces.isNotEmpty && provinceIndex.value < provinces.length) { final selectedProvince = provinces[provinceIndex.value]; for (var province in country.children ?? []) { final provinceName = province.value ?? province.province; if (provinceName == selectedProvince) { if (cities.isNotEmpty && cityIndex.value < cities.length) { final selectedCityName = cities[cityIndex.value]; for (var city in province.children ?? []) { final cityName = city.value ?? city.city; if (cityName == selectedCityName) { return city; } } } if ((province.children == null || province.children!.isEmpty) && (province.city != null || province.value != null)) { return province; } } } } return country; } } return null; } catch (e) { ef.log("获取选中城市数据失败:$e"); return null; } } return Container( width: double.infinity, decoration: BoxDecoration( color: pickerBackgroundColor, // 使用传入的选择器整体颜色 borderRadius: BorderRadius.only( topLeft: Radius.circular(20.rpx), topRight: Radius.circular(20.rpx), ), ), constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.65, ), child: Padding( padding: EdgeInsets.fromLTRB(30.rpx, 10.rpx, 30.rpx, bottomInsets + 10.rpx), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.transparent, padding: EdgeInsets.zero, onTap: () => Navigator.of(context).pop(), child: Container( width: 110.rpx, height: 60.rpx, alignment: Alignment.center, child: Text("取消".tr, style: TextStyle(fontSize: 30.rpx, color: Colors.white)), ), ), Text( title, style: TextStyle( fontFamily: 'Readex Pro', color: themeController.currentColor.sc3, fontSize: 30.rpx, ), ), Obx(() { CityModelController cityModelController = Get.find(); cityModelController.tmp; ef.log("${cityModelController.tmp.value}"); return ClickableContainer( backgroundColor: Colors.transparent, highlightColor: Colors.transparent, padding: EdgeInsets.zero, onTap: () { final selectedCityData = getSelectedCityData(); if (selectedCityData != null) { final fullCityData = findCompleteCityDataById( selectedCityData.id, cityData); if (fullCityData != null) { onCityChanged?.call(fullCityData); } else { onCityChanged?.call(selectedCityData); } } Navigator.of(context).pop(); }, child: Container( width: 110.rpx, height: 60.rpx, alignment: Alignment.center, child: Text("确定".tr, style: TextStyle( fontSize: 30.rpx, color: confirmTextColor, // 使用传入的确定文字颜色 )), ), ); }), ], ), Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 20.rpx, 0, 10.rpx), child: ListSearchWidget( keyword: cityModelController.model.keyword, color: cityModelController.model.color, hint: "输入国家、省份或城市".tr, onChange: (d) { cityModelController.model.keyword = d; cityModelController.searchCities(d); }, findCallback: () { cityModelController .searchCities(cityModelController.model.keyword ?? ""); }, padding: EdgeInsets.fromLTRB(0.rpx, 0.rpx, 0.rpx, 10.rpx), showResultList: true, searchResults: cityModelController.searchResults, onResultTap: (result) { final selectedCity = cityModelController.getCityByDisplayName(result); if (selectedCity != null) { final fullCityData = findCompleteCityDataById(selectedCity.id, cityData); PersonController personController = Get.find(); personController.cityModel = fullCityData; personController.updateAll(); if (onCityChanged != null) { onCityChanged(fullCityData); } Navigator.of(context).pop(); } }, ), ), // 选择器区域 Expanded( child: Stack( children: [ Positioned.fill( child: IgnorePointer( child: Center( child: Container( height: 90.rpx, margin: EdgeInsets.symmetric(horizontal: 0.rpx), decoration: BoxDecoration( color: selectedCityColor, // 使用传入的选中城市颜色 borderRadius: BorderRadius.circular(16.rpx), ), ), ), ), ), Container( height: double.infinity, child: Padding( padding: EdgeInsets.fromLTRB(20.rpx, 0, 20.rpx, 0), child: Row( children: [ Expanded( child: Obx(() { CityModelController cityModelController = Get.find(); cityModelController.tmp; ef.log("${cityModelController.tmp.value}"); return getOnePickers( context, countries, countryIndex, unit: "", onChanged: (_) => updateProvinces(), pickerKey: ValueKey( 'country_${DateTime.now().millisecondsSinceEpoch}'), selectedColor: selectedTextColor, // 传递选中文字颜色 ); }), ), Expanded( child: Obx(() { CityModelController cityModelController = Get.find(); cityModelController.tmp; ef.log("${cityModelController.tmp.value}"); return getOnePickers( context, provinces, provinceIndex, unit: "", onChanged: (_) => updateCities(), pickerKey: ValueKey( 'province_${DateTime.now().millisecondsSinceEpoch}'), selectedColor: selectedTextColor, // 传递选中文字颜色 ); }), ), Expanded( child: Obx(() { CityModelController cityModelController = Get.find(); cityModelController.tmp; ef.log("${cityModelController.tmp.value}"); return getOnePickers( context, cities, cityIndex, unit: "", pickerKey: ValueKey( 'city_${DateTime.now().millisecondsSinceEpoch}'), selectedColor: selectedTextColor, // 传递选中文字颜色 ); }), ), ], ), ), ), ], ), ), ], ), ), ); } // 加载中 BottomSheet Widget _buildLoadingBottomSheet( ThemeController themeController, Color pickerBackgroundColor, Color confirmTextColor, ) { return Container( width: double.infinity, decoration: BoxDecoration( color: pickerBackgroundColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(20.rpx), topRight: Radius.circular(20.rpx), ), ), padding: EdgeInsets.fromLTRB(30.rpx, 40.rpx, 30.rpx, 90.rpx), child: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator( color: confirmTextColor, ), SizedBox(height: 20.rpx), Text( "加载中...".tr, style: TextStyle( color: themeController.currentColor.sc3, fontSize: 28.rpx, ), ), ], ), ); } // 错误 BottomSheet Widget _buildErrorBottomSheet( ThemeController themeController, BuildContext context, Color pickerBackgroundColor, Color confirmTextColor, ) { return Container( width: double.infinity, decoration: BoxDecoration( color: pickerBackgroundColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(20.rpx), topRight: Radius.circular(20.rpx), ), ), padding: EdgeInsets.fromLTRB(30.rpx, 40.rpx, 30.rpx, 90.rpx), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( "数据加载失败".tr, style: TextStyle( color: themeController.currentColor.sc3, fontSize: 28.rpx, ), ), SizedBox(height: 20.rpx), ClickableContainer( onTap: () => Navigator.of(context).pop(), backgroundColor: Colors.transparent, highlightColor: Colors.transparent, padding: EdgeInsets.all(0), child: Container( padding: EdgeInsets.symmetric(horizontal: 30.rpx, vertical: 15.rpx), decoration: BoxDecoration( color: confirmTextColor, borderRadius: BorderRadius.circular(8.rpx), ), child: Text( "关闭".tr, style: TextStyle( color: Colors.white, fontSize: 28.rpx, ), ), ), ), ], ), ); }