Files
tuiche/lib/pages/person/select_city.dart
2025-12-17 17:24:00 +08:00

804 lines
26 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<CityModel>
Future<List<CityModel>> 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<dynamic> 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; // 选中的城市背景色
const CitySelectionColors({
this.pickerBackgroundColor,
this.confirmTextColor,
this.selectedCityColor,
});
}
Future showCitySelectionDialog(
BuildContext context, {
required CityModel selectedCity,
Function? onCityChanged,
String title = "选择城市",
required Future<List<CityModel>> 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 RxList<String> countries = <String>[].obs;
final RxList<String> provinces = <String>[].obs;
final RxList<String> cities = <String>[].obs;
final RxInt countryIndex = 0.obs;
final RxInt provinceIndex = 0.obs;
final RxInt cityIndex = 0.obs;
cityModelController.searchResults = [];
// 更新城市列表
void updateCities(List<CityModel> 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<String> 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<CityModel> cityData) {
try {
if (countries.isEmpty) return;
final selectedCountry = countries[countryIndex.value];
// 在层级结构中查找对应的省份
for (var country in cityData) {
if (country.value == selectedCountry) {
// 安全地处理省份列表,过滤掉 null 值并转换为 String
final List<String> 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<CityModel> cityData) {
try {
// 安全地处理国家列表,过滤掉 null 值并转换为 String
final List<String> 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<CityModel> 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<List<CityModel>>(
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,
countries: countries,
provinces: provinces,
cities: cities,
countryIndex: countryIndex,
provinceIndex: provinceIndex,
cityIndex: cityIndex,
);
},
),
],
),
);
},
);
}
// 根据ID查找完整的城市数据包含国家、省份、城市信息
CityModel? findCompleteCityDataById(int? id, List<CityModel> 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<CityModel> cityData,
Function? onCityChanged, {
required Color pickerBackgroundColor,
required Color confirmTextColor,
required Color selectedCityColor,
required RxList<String> countries,
required RxList<String> provinces,
required RxList<String> 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<String>();
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<String> 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: <Widget>[
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();
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}'), // 添加动态 key
);
}),
),
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}'), // 添加动态 key
);
}),
),
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}'), // 添加动态 key
);
}),
),
],
),
),
),
],
),
),
],
),
),
);
}
// 加载中 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,
),
),
),
),
],
),
);
}