1456 lines
60 KiB
Dart
1456 lines
60 KiB
Dart
import 'dart:convert';
|
||
import 'dart:typed_data';
|
||
|
||
import 'package:ef/ef.dart';
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:vbvs_app/common/color/appConstants.dart';
|
||
import 'package:vbvs_app/common/pojo/city.dart';
|
||
import 'package:vbvs_app/common/util/FitTool.dart';
|
||
import 'package:vbvs_app/common/util/MyUtils.dart';
|
||
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
|
||
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
|
||
import 'package:vbvs_app/controller/user_info_controller.dart';
|
||
import 'package:vbvs_app/language/AppLanguage.dart';
|
||
import 'package:vbvs_app/pages/common/selectDialog.dart';
|
||
|
||
// Future showDateSelectionDialog(BuildContext context,
|
||
// {required DateTime checkDate, Function? checkChange, String title = "生日"}) {
|
||
// ThemeController themeController = Get.find();
|
||
// List years = [], months = [], days = [];
|
||
// var days_select = [].obs;
|
||
// int day_len = 31;
|
||
// int year = DateTime.now().year;
|
||
// for (var i = 0; i < 100; i++) {
|
||
// years.insert(0, year - i);
|
||
// }
|
||
// for (var i = 1; i < 13; i++) {
|
||
// months.add(i);
|
||
// }
|
||
// for (var i = 1; i < 32; i++) {
|
||
// days.add(i);
|
||
// }
|
||
// int yearIndex = years.lastIndexOf(checkDate.year);
|
||
// int monthIndex = months.lastIndexOf(checkDate.month);
|
||
// day_len = DateTime.fromMillisecondsSinceEpoch(
|
||
// DateTime(years[yearIndex], months[monthIndex] + 1)
|
||
// .millisecondsSinceEpoch -
|
||
// 1000)
|
||
// .day;
|
||
// days_select.value = days.sublist(0, day_len);
|
||
// int dayIndex = days.lastIndexOf(checkDate.day);
|
||
// return showDialog(
|
||
// context: context,
|
||
// barrierDismissible: true, // 点击对话框外部可关闭
|
||
// builder: (BuildContext context) {
|
||
// return Stack(
|
||
// children: [
|
||
// Positioned(
|
||
// bottom: 0, // 控制弹窗距离顶部的位置
|
||
// left: 0,
|
||
// right: 0,
|
||
// child: Material(
|
||
// color: Colors.transparent,
|
||
// child: Dialog(
|
||
// backgroundColor: themeController.currentColor.sc17,
|
||
// insetPadding: EdgeInsets.zero,
|
||
// shape: RoundedRectangleBorder(
|
||
// borderRadius: BorderRadius.circular(0),
|
||
// ),
|
||
// child: Container(
|
||
// width: double.infinity,
|
||
// decoration: BoxDecoration(
|
||
// borderRadius: BorderRadius.only(
|
||
// topLeft: Radius.circular(
|
||
// AppConstants().normal_container_radius),
|
||
// topRight: Radius.circular(
|
||
// AppConstants().normal_container_radius),
|
||
// bottomLeft: Radius.circular(0.rpx),
|
||
// bottomRight: Radius.circular(0.rpx),
|
||
// ),
|
||
// ),
|
||
// child: Column(
|
||
// mainAxisSize: MainAxisSize.min,
|
||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||
// children: <Widget>[
|
||
// Container(
|
||
// color: themeController.currentColor.sc5,
|
||
// alignment: Alignment.centerLeft,
|
||
// height: 80.rpx,
|
||
// child: Padding(
|
||
// padding: EdgeInsets.only(left: 30.rpx, right: 30.rpx),
|
||
// child: Row(
|
||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||
// children: [
|
||
// InkWell(
|
||
// child: Text(
|
||
// "日期.取消".tr,
|
||
// style: TextStyle(
|
||
// fontFamily: 'Readex Pro',
|
||
// color: themeController.currentColor.sc3,
|
||
// letterSpacing: 0,
|
||
// fontSize:
|
||
// AppConstants().normal_text_fontSize),
|
||
// ),
|
||
// onTap: () {
|
||
// Get.back();
|
||
// },
|
||
// ),
|
||
// Text(
|
||
// "$title",
|
||
// style: TextStyle(
|
||
// fontFamily: 'Readex Pro',
|
||
// color: themeController.currentColor.sc3,
|
||
// letterSpacing: 0,
|
||
// fontSize:
|
||
// AppConstants().title_text_fontSize),
|
||
// ),
|
||
// // closeIconWhite,
|
||
// InkWell(
|
||
// child: Text(
|
||
// "日期.确定".tr,
|
||
// style: TextStyle(
|
||
// fontFamily: 'Readex Pro',
|
||
// color: themeController.currentColor.sc2,
|
||
// letterSpacing: 0,
|
||
// fontSize:
|
||
// AppConstants().normal_text_fontSize),
|
||
// ),
|
||
// onTap: () {
|
||
// checkChange?.call(DateTime(years[yearIndex],
|
||
// months[monthIndex], days[dayIndex]));
|
||
// Get.back();
|
||
// },
|
||
// )
|
||
// ],
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// // Container(
|
||
// // height: 240.rpx,
|
||
// // margin: EdgeInsets.only(top: 60.rpx, bottom: 60.rpx),
|
||
// // padding: EdgeInsets.symmetric(horizontal: 30.rpx),
|
||
// // child: Row(
|
||
// // mainAxisAlignment: MainAxisAlignment.center, // ✅ 整体居中
|
||
// // crossAxisAlignment: CrossAxisAlignment.center,
|
||
// // children: [
|
||
// // Row(
|
||
// // children: [
|
||
// // SizedBox(
|
||
// // width: 120.rpx,
|
||
// // child: getOnePicker(context, years, yearIndex,
|
||
// // (d) {
|
||
// // yearIndex = d;
|
||
// // dayIndex = 0;
|
||
// // day_len =
|
||
// // DateTime.fromMillisecondsSinceEpoch(
|
||
// // DateTime(
|
||
// // years[yearIndex],
|
||
// // months[monthIndex] +
|
||
// // 1)
|
||
// // .millisecondsSinceEpoch -
|
||
// // 1000)
|
||
// // .day;
|
||
// // days_select.value =
|
||
// // days.sublist(0, day_len);
|
||
// // }, "".tr),
|
||
// // ),
|
||
// // ],
|
||
// // ),
|
||
|
||
// // SizedBox(width: 100.rpx),
|
||
|
||
// // // 月
|
||
// // Row(
|
||
// // children: [
|
||
// // SizedBox(
|
||
// // width: 80.rpx,
|
||
// // child: getOnePicker(
|
||
// // context, months, monthIndex, (d) {
|
||
// // monthIndex = d;
|
||
// // dayIndex = 0;
|
||
// // day_len =
|
||
// // DateTime.fromMillisecondsSinceEpoch(
|
||
// // DateTime(
|
||
// // years[yearIndex],
|
||
// // months[monthIndex] +
|
||
// // 1)
|
||
// // .millisecondsSinceEpoch -
|
||
// // 1000)
|
||
// // .day;
|
||
// // days_select.value =
|
||
// // days.sublist(0, day_len);
|
||
// // }, "".tr),
|
||
// // ),
|
||
// // ],
|
||
// // ),
|
||
|
||
// // SizedBox(width: 100.rpx),
|
||
|
||
// // Row(
|
||
// // children: [
|
||
// // SizedBox(
|
||
// // width: 80.rpx,
|
||
// // child: Obx(() {
|
||
// // return getOnePicker(
|
||
// // context, days_select, dayIndex, (d) {
|
||
// // dayIndex = d;
|
||
// // }, "".tr);
|
||
// // }),
|
||
// // ),
|
||
// // ],
|
||
// // ),
|
||
// // ],
|
||
// // ),
|
||
// // )
|
||
// //-----
|
||
// Container(
|
||
// height: 240.rpx,
|
||
// margin: EdgeInsets.only(top: 60.rpx, bottom: 60.rpx),
|
||
// padding: EdgeInsets.symmetric(horizontal: 30.rpx),
|
||
// child: Stack(
|
||
// children: [
|
||
// // ✅ 选中行背景色
|
||
// Positioned.fill(
|
||
// child: IgnorePointer(
|
||
// child: Center(
|
||
// child: Container(
|
||
// height: 90.rpx, // 对齐选中行高度
|
||
// margin: EdgeInsets.symmetric(
|
||
// horizontal: 10.rpx),
|
||
// decoration: BoxDecoration(
|
||
// color: themeController.currentColor.sc2,
|
||
// borderRadius:
|
||
// BorderRadius.circular(16.rpx),
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// ),
|
||
|
||
// // ✅ 三列 Picker
|
||
// Row(
|
||
// mainAxisAlignment:
|
||
// MainAxisAlignment.center, // 整体居中
|
||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||
// children: [
|
||
// // 年
|
||
// SizedBox(
|
||
// width: 120.rpx,
|
||
// child: getOnePicker(context, years, yearIndex,
|
||
// (d) {
|
||
// yearIndex = d;
|
||
// dayIndex = 0;
|
||
// day_len =
|
||
// DateTime.fromMillisecondsSinceEpoch(
|
||
// DateTime(
|
||
// years[yearIndex],
|
||
// months[monthIndex] +
|
||
// 1)
|
||
// .millisecondsSinceEpoch -
|
||
// 1000)
|
||
// .day;
|
||
// days_select.value =
|
||
// days.sublist(0, day_len);
|
||
// }, "".tr),
|
||
// ),
|
||
|
||
// SizedBox(width: 100.rpx),
|
||
|
||
// // 月
|
||
// SizedBox(
|
||
// width: 80.rpx,
|
||
// child: getOnePicker(
|
||
// context, months, monthIndex, (d) {
|
||
// monthIndex = d;
|
||
// dayIndex = 0;
|
||
// day_len =
|
||
// DateTime.fromMillisecondsSinceEpoch(
|
||
// DateTime(
|
||
// years[yearIndex],
|
||
// months[monthIndex] +
|
||
// 1)
|
||
// .millisecondsSinceEpoch -
|
||
// 1000)
|
||
// .day;
|
||
// days_select.value =
|
||
// days.sublist(0, day_len);
|
||
// }, "".tr),
|
||
// ),
|
||
|
||
// SizedBox(width: 100.rpx),
|
||
|
||
// // 日
|
||
// SizedBox(
|
||
// width: 80.rpx,
|
||
// child: Obx(() {
|
||
// return getOnePicker(
|
||
// context, days_select, dayIndex, (d) {
|
||
// dayIndex = d;
|
||
// }, "".tr);
|
||
// }),
|
||
// ),
|
||
// ],
|
||
// ),
|
||
// ],
|
||
// ),
|
||
// ),
|
||
|
||
// ],
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// ],
|
||
// );
|
||
// },
|
||
// );
|
||
// }
|
||
|
||
Future showDateSelectionDialog(BuildContext context,
|
||
{required DateTime checkDate,
|
||
Function? checkChange,
|
||
String title = "选择生日"}) {
|
||
ThemeController themeController = Get.find();
|
||
final bool isEn = Get.locale?.languageCode.startsWith('en') ?? false;
|
||
Color checkColor = stringToColor("#D3B684");
|
||
|
||
final List<int> years = List.generate(100, (i) => DateTime.now().year - i)
|
||
..sort();
|
||
final List<int> months = List.generate(12, (i) => i + 1);
|
||
final List<int> days = List.generate(31, (i) => i + 1);
|
||
|
||
final RxList<int> daysSelect = <int>[].obs;
|
||
|
||
final RxInt yearIndex = years.indexOf(checkDate.year).obs;
|
||
final RxInt monthIndex = months.indexOf(checkDate.month).obs;
|
||
final RxInt dayIndex = days.indexOf(checkDate.day).obs;
|
||
|
||
void updateDays() {
|
||
final int daysInMonth =
|
||
DateTime(years[yearIndex.value], months[monthIndex.value] + 1, 0).day;
|
||
daysSelect.value = days.sublist(0, daysInMonth);
|
||
if (dayIndex.value >= daysInMonth) {
|
||
dayIndex.value = daysInMonth - 1;
|
||
}
|
||
}
|
||
|
||
updateDays();
|
||
|
||
return showDialog(
|
||
context: context,
|
||
barrierDismissible: true,
|
||
builder: (BuildContext context) {
|
||
return Stack(
|
||
children: [
|
||
Positioned(
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Dialog(
|
||
backgroundColor: themeController.currentColor.sc17,
|
||
insetPadding: EdgeInsets.zero,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(0),
|
||
),
|
||
child: Container(
|
||
width: double.infinity,
|
||
padding: EdgeInsets.fromLTRB(30.rpx, 10.rpx, 30.rpx, 90.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,
|
||
),
|
||
),
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.zero,
|
||
onTap: () {
|
||
final selectedDate = DateTime(
|
||
years[yearIndex.value],
|
||
months[monthIndex.value],
|
||
daysSelect[dayIndex.value],
|
||
);
|
||
checkChange?.call(selectedDate);
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
alignment: Alignment.center,
|
||
child: Text("确定".tr,
|
||
style: TextStyle(
|
||
fontSize: 30.rpx,
|
||
color: themeController.currentColor.sc2,
|
||
)),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
SizedBox(height: 20.rpx),
|
||
Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: IgnorePointer(
|
||
child: Center(
|
||
child: Container(
|
||
height: 90.rpx,
|
||
margin:
|
||
EdgeInsets.symmetric(horizontal: 70.rpx),
|
||
decoration: BoxDecoration(
|
||
color: themeController.currentColor.sc2,
|
||
borderRadius: BorderRadius.circular(16.rpx),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(
|
||
height: 240.rpx,
|
||
child: Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 95.rpx),
|
||
child: Row(
|
||
children: isEn
|
||
? [
|
||
Expanded(
|
||
child: getOnePickers(
|
||
context,
|
||
months,
|
||
monthIndex,
|
||
isMonthName: true,
|
||
onChanged: (_) => updateDays(),
|
||
),
|
||
),
|
||
Expanded(
|
||
child: getOnePickers(
|
||
context,
|
||
daysSelect,
|
||
dayIndex,
|
||
),
|
||
),
|
||
Expanded(
|
||
child: getOnePickers(
|
||
context,
|
||
years,
|
||
yearIndex,
|
||
onChanged: (_) => updateDays(),
|
||
),
|
||
),
|
||
]
|
||
: [
|
||
Expanded(
|
||
child: getOnePickers(
|
||
context,
|
||
years,
|
||
yearIndex,
|
||
unit: "年",
|
||
onChanged: (_) => updateDays(),
|
||
),
|
||
),
|
||
Expanded(
|
||
child: getOnePickers(
|
||
context,
|
||
months,
|
||
monthIndex,
|
||
unit: "月",
|
||
onChanged: (_) => updateDays(),
|
||
),
|
||
),
|
||
Expanded(
|
||
child: getOnePickers(
|
||
context,
|
||
daysSelect,
|
||
dayIndex,
|
||
unit: "日",
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
TextStyle _unitStyle(BuildContext context) {
|
||
return TextStyle(
|
||
fontFamily: 'Readex Pro',
|
||
color: themeController.currentColor.sc3,
|
||
fontSize: 30.rpx,
|
||
letterSpacing: 0,
|
||
);
|
||
}
|
||
|
||
getOnePicker(BuildContext context, List arr, int checkIndex,
|
||
Function onSelectedItemChanged, String unit,
|
||
{bool looping = false}) {
|
||
ThemeController themeController = Get.find();
|
||
|
||
return CupertinoPicker(
|
||
key: UniqueKey(),
|
||
useMagnifier: false,
|
||
itemExtent: 80.rpx,
|
||
magnification: 1,
|
||
diameterRatio: 3,
|
||
squeeze: 1,
|
||
looping: looping,
|
||
scrollController: FixedExtentScrollController(initialItem: checkIndex),
|
||
selectionOverlay: Container(), // 不要默认选中遮罩
|
||
onSelectedItemChanged: (int value) {
|
||
onSelectedItemChanged.call(value);
|
||
},
|
||
children: List.generate(arr.length, (index) {
|
||
return Container(
|
||
alignment: Alignment.center,
|
||
child: Text(
|
||
"${arr[index]}$unit", // ✅ 每项都带单位
|
||
style: TextStyle(
|
||
fontFamily: 'Readex Pro',
|
||
color: themeController.currentColor.sc3,
|
||
fontSize: 30.rpx,
|
||
),
|
||
),
|
||
);
|
||
}),
|
||
);
|
||
}
|
||
|
||
Future<void> showHeightPickerDialog(
|
||
BuildContext context, {
|
||
required int initialHeight, // 初始身高(单位:cm)
|
||
required Function(int selectedHeight) onConfirm,
|
||
String title = "选择身高",
|
||
}) async {
|
||
List<int> heights = List.generate(101, (index) => 120 + index); // 120~220cm
|
||
int selectedIndex = heights.indexOf(initialHeight);
|
||
// int tempIndex = selectedIndex;
|
||
final RxInt tempIndex = RxInt(selectedIndex); // ✅ 改为 RxInt
|
||
ThemeController themeController = Get.find();
|
||
await showDialog(
|
||
context: context,
|
||
barrierDismissible: true,
|
||
builder: (BuildContext context) {
|
||
return Stack(
|
||
children: [
|
||
Positioned(
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Dialog(
|
||
backgroundColor: themeController.currentColor.sc17,
|
||
insetPadding: EdgeInsets.zero,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(0),
|
||
),
|
||
child: Container(
|
||
padding: EdgeInsets.fromLTRB(0.rpx, 0.rpx, 0.rpx, 90.rpx),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Container(
|
||
padding:
|
||
EdgeInsets.fromLTRB(30.rpx, 0.rpx, 30.rpx, 0.rpx),
|
||
color: themeController.currentColor.sc5,
|
||
height: 80.rpx,
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.only(top: 0),
|
||
onTap: () {
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
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,
|
||
)),
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.only(top: 0),
|
||
onTap: () {
|
||
onConfirm(
|
||
heights[tempIndex.value]); // ✅ 使用 .value
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"确定".tr,
|
||
style: TextStyle(
|
||
fontSize: 30.rpx,
|
||
color: themeController.currentColor.sc2,
|
||
),
|
||
),
|
||
)),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 20.rpx),
|
||
Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: IgnorePointer(
|
||
child: Center(
|
||
child: Container(
|
||
height: 90.rpx,
|
||
margin:
|
||
EdgeInsets.symmetric(horizontal: 95.rpx),
|
||
decoration: BoxDecoration(
|
||
color: themeController.currentColor.sc2,
|
||
borderRadius: BorderRadius.circular(16.rpx),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(
|
||
height: 240.rpx,
|
||
child: getOnePickers(
|
||
context,
|
||
heights,
|
||
tempIndex, // ✅ 传入 RxInt
|
||
unit: "cm",
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
Future<void> showWeightPickerDialog(
|
||
BuildContext context, {
|
||
required String initialWeight, // 初始体重(单位:kg)
|
||
required Function(int selectedWeight) onConfirm,
|
||
String title = "选择体重",
|
||
}) async {
|
||
List<int> weights = List.generate(151, (index) => 30 + index); // 30~180kg
|
||
int selectedIndex = weights.indexOf(int.tryParse(initialWeight) ?? 50);
|
||
final RxInt tempIndex = RxInt(selectedIndex); // ✅ 改为 RxInt
|
||
|
||
ThemeController themeController = Get.find();
|
||
|
||
await showDialog(
|
||
context: context,
|
||
barrierDismissible: true,
|
||
builder: (BuildContext context) {
|
||
return Stack(
|
||
children: [
|
||
Positioned(
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Dialog(
|
||
backgroundColor: themeController.currentColor.sc17,
|
||
insetPadding: EdgeInsets.zero,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(0),
|
||
),
|
||
child: Container(
|
||
padding: EdgeInsets.fromLTRB(0.rpx, 0.rpx, 0.rpx, 90.rpx),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
// 标题 + 按钮
|
||
Container(
|
||
padding:
|
||
EdgeInsets.fromLTRB(30.rpx, 0.rpx, 30.rpx, 0.rpx),
|
||
color: themeController.currentColor.sc5,
|
||
height: 80.rpx,
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.only(top: 0),
|
||
onTap: () {
|
||
Navigator.of(context).pop();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"取消".tr,
|
||
style: TextStyle(
|
||
fontSize: 30.rpx,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
)),
|
||
Text(title.tr,
|
||
style: TextStyle(
|
||
fontFamily: 'Readex Pro',
|
||
color: themeController.currentColor.sc3,
|
||
fontSize: 30.rpx,
|
||
)),
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.only(top: 0),
|
||
onTap: () {
|
||
onConfirm(
|
||
weights[tempIndex.value]); // ✅ 回调返回选中值
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"确定".tr,
|
||
style: TextStyle(
|
||
fontSize: 30.rpx,
|
||
color: themeController.currentColor.sc2,
|
||
),
|
||
),
|
||
)),
|
||
],
|
||
),
|
||
),
|
||
|
||
SizedBox(height: 20.rpx),
|
||
Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: IgnorePointer(
|
||
child: Center(
|
||
child: Container(
|
||
height: 90.rpx,
|
||
margin:
|
||
EdgeInsets.symmetric(horizontal: 95.rpx),
|
||
decoration: BoxDecoration(
|
||
color: themeController.currentColor.sc2,
|
||
borderRadius: BorderRadius.circular(16.rpx),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(
|
||
height: 240.rpx,
|
||
child: getOnePickers(
|
||
context,
|
||
weights,
|
||
tempIndex, // ✅ 传入 RxInt
|
||
unit: "kg",
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
Future<void> showScorePickerDialog(
|
||
BuildContext context, {
|
||
required int initialHeight,
|
||
required Function(int selectedHeight) onConfirm,
|
||
String title = "选择分数",
|
||
String unit = "", // 单位(可选)
|
||
int min = 0, // ✅ 新增:最小值
|
||
int max = 100, // ✅ 新增:最大值
|
||
}) async {
|
||
/// 生成范围:min ~ max
|
||
List<int> heights = List.generate(max - min + 1, (index) => min + index);
|
||
|
||
/// 确保初始值在范围内
|
||
if (initialHeight < min) initialHeight = min;
|
||
if (initialHeight > max) initialHeight = max;
|
||
|
||
int selectedIndex = heights.indexOf(initialHeight);
|
||
final RxInt tempIndex = RxInt(selectedIndex);
|
||
|
||
ThemeController themeController = Get.find();
|
||
|
||
await showDialog(
|
||
context: context,
|
||
barrierDismissible: true,
|
||
builder: (BuildContext context) {
|
||
return Stack(
|
||
children: [
|
||
Positioned(
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Dialog(
|
||
backgroundColor: themeController.currentColor.sc17,
|
||
insetPadding: EdgeInsets.zero,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(0),
|
||
),
|
||
child: Container(
|
||
padding: EdgeInsets.fromLTRB(0.rpx, 0.rpx, 0.rpx, 90.rpx),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
// ------- 顶部标题区域 -------
|
||
Container(
|
||
padding:
|
||
EdgeInsets.fromLTRB(30.rpx, 0.rpx, 30.rpx, 0.rpx),
|
||
color: themeController.currentColor.sc5,
|
||
height: 80.rpx,
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.only(top: 0),
|
||
onTap: () {
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
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,
|
||
),
|
||
),
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.only(top: 0),
|
||
onTap: () {
|
||
onConfirm(heights[tempIndex.value]);
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"确定".tr,
|
||
style: TextStyle(
|
||
fontSize: 30.rpx,
|
||
color: themeController.currentColor.sc2,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
SizedBox(height: 20.rpx),
|
||
|
||
// -------- 中间选择器 --------
|
||
Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: IgnorePointer(
|
||
child: Center(
|
||
child: Container(
|
||
height: 90.rpx,
|
||
margin:
|
||
EdgeInsets.symmetric(horizontal: 95.rpx),
|
||
decoration: BoxDecoration(
|
||
color: themeController.currentColor.sc2,
|
||
borderRadius: BorderRadius.circular(16.rpx),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(
|
||
height: 240.rpx,
|
||
child: getOnePickers(
|
||
context,
|
||
heights,
|
||
tempIndex,
|
||
unit: unit, // 使用传入的单位
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
Future<void> showIntervalPickerDialog(
|
||
BuildContext context, {
|
||
required int initialValue,
|
||
required Function(int selectedValue) onConfirm,
|
||
List<int> options = const [], // ✅ 可选值数组(优先级最高)
|
||
int min = 0, // 若没有 options,则使用 min/max
|
||
int max = 100,
|
||
int type = 2, // 1=秒,2=分钟(默认),3=小时
|
||
String unit = "分钟/次",
|
||
String title = "选择间隔",
|
||
}) async {
|
||
ThemeController themeController = Get.find();
|
||
|
||
/// ------------------------------------------
|
||
/// 计算实际数据源:优先使用 options
|
||
/// ------------------------------------------
|
||
List<int> values;
|
||
if (options.isNotEmpty) {
|
||
values = [...options];
|
||
} else {
|
||
values = List.generate(max - min + 1, (index) => min + index);
|
||
}
|
||
|
||
/// ------------------------------------------
|
||
/// 显示转换函数
|
||
/// ------------------------------------------
|
||
String formatValue(int raw) {
|
||
double result;
|
||
|
||
switch (type) {
|
||
case 1: // 秒
|
||
result = raw.toDouble();
|
||
return "${result.toInt()}秒/次";
|
||
|
||
case 2: // 分钟
|
||
result = raw / 60.0;
|
||
break;
|
||
|
||
case 3: // 小时
|
||
result = raw / 3600.0;
|
||
break;
|
||
|
||
default:
|
||
result = raw.toDouble();
|
||
}
|
||
|
||
/// 去掉多余的小数,例如 1.0 → 1
|
||
String text =
|
||
result % 1 == 0 ? result.toInt().toString() : result.toStringAsFixed(1);
|
||
|
||
return "$text$unit";
|
||
}
|
||
|
||
/// ------------------------------------------
|
||
/// 初始 index
|
||
/// ------------------------------------------
|
||
int initialIndex = values.indexOf(initialValue);
|
||
if (initialIndex < 0) initialIndex = 0;
|
||
final RxInt tempIndex = RxInt(initialIndex);
|
||
await showDialog(
|
||
context: context,
|
||
barrierDismissible: true,
|
||
builder: (BuildContext context) {
|
||
return Stack(
|
||
children: [
|
||
Positioned(
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Dialog(
|
||
backgroundColor: themeController.currentColor.sc17,
|
||
insetPadding: EdgeInsets.zero,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(0),
|
||
),
|
||
child: Container(
|
||
padding: EdgeInsets.fromLTRB(0.rpx, 0.rpx, 0.rpx, 90.rpx),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
/// ---------------- 顶部标题 ----------------
|
||
Container(
|
||
padding:
|
||
EdgeInsets.fromLTRB(30.rpx, 0.rpx, 30.rpx, 0.rpx),
|
||
color: themeController.currentColor.sc5,
|
||
height: 80.rpx,
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
onTap: () => Get.back(),
|
||
padding: EdgeInsets.all(0),
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
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,
|
||
),
|
||
),
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
onTap: () {
|
||
onConfirm(values[tempIndex.value]); // 返回原始秒数
|
||
Get.back();
|
||
},
|
||
padding: EdgeInsets.all(0),
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"确定".tr,
|
||
style: TextStyle(
|
||
fontSize: 30.rpx,
|
||
color: themeController.currentColor.sc2,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
SizedBox(height: 20.rpx),
|
||
|
||
/// ---------------- 中间选择器 ----------------
|
||
Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: IgnorePointer(
|
||
child: Center(
|
||
child: Container(
|
||
height: 90.rpx,
|
||
margin:
|
||
EdgeInsets.symmetric(horizontal: 95.rpx),
|
||
decoration: BoxDecoration(
|
||
color: themeController.currentColor.sc2,
|
||
borderRadius: BorderRadius.circular(16.rpx),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(
|
||
height: 240.rpx,
|
||
child: getOnePickersSpe(
|
||
context,
|
||
values,
|
||
tempIndex,
|
||
/// → 替换显示文本(核心)
|
||
customDisplay: (val) => formatValue(val),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
|
||
Future<void> showCountryCodePickerDialog(
|
||
BuildContext context, {
|
||
required String initialCode, // 初始区号,如 "+86"
|
||
required Function(String selectedCode) onConfirm,
|
||
String title = "选择区号",
|
||
}) async {
|
||
UserInfoController controller = Get.find();
|
||
ThemeController themeController = Get.find();
|
||
|
||
final List<Map<String, String>> list = controller.country_code;
|
||
|
||
int selectedIndex =
|
||
list.indexWhere((e) => e["id"] == initialCode);
|
||
if (selectedIndex < 0) selectedIndex = 0;
|
||
|
||
final RxInt tempIndex = RxInt(selectedIndex);
|
||
|
||
await showDialog(
|
||
context: context,
|
||
barrierDismissible: true,
|
||
builder: (BuildContext context) {
|
||
return Stack(
|
||
children: [
|
||
Positioned(
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Dialog(
|
||
backgroundColor: themeController.currentColor.sc17,
|
||
insetPadding: EdgeInsets.zero,
|
||
shape: const RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.zero,
|
||
),
|
||
child: Container(
|
||
padding: EdgeInsets.fromLTRB(0, 0, 0, 90.rpx),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
/// 顶部栏
|
||
Container(
|
||
padding:
|
||
EdgeInsets.symmetric(horizontal: 30.rpx),
|
||
color: themeController.currentColor.sc5,
|
||
height: 80.rpx,
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
onTap: Get.back,
|
||
padding: EdgeInsets.all(0),
|
||
child: SizedBox(
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
child: Center(
|
||
child: Text(
|
||
"取消".tr,
|
||
style: const TextStyle(
|
||
color: Colors.white),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
Text(
|
||
title.tr,
|
||
style: TextStyle(
|
||
fontFamily: 'Readex Pro',
|
||
color:
|
||
themeController.currentColor.sc3,
|
||
fontSize: 30.rpx,
|
||
),
|
||
),
|
||
ClickableContainer(
|
||
padding: EdgeInsets.all(0),
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
onTap: () {
|
||
onConfirm(
|
||
list[tempIndex.value]["id"]!);
|
||
Get.back();
|
||
},
|
||
child: SizedBox(
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
child: Center(
|
||
child: Text(
|
||
"确定".tr,
|
||
style: TextStyle(
|
||
color: themeController
|
||
.currentColor.sc2,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
SizedBox(height: 20.rpx),
|
||
|
||
/// Picker
|
||
Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: IgnorePointer(
|
||
child: Center(
|
||
child: Container(
|
||
height: 90.rpx,
|
||
margin: EdgeInsets.symmetric(
|
||
horizontal: 95.rpx),
|
||
decoration: BoxDecoration(
|
||
color: themeController
|
||
.currentColor.sc2,
|
||
borderRadius:
|
||
BorderRadius.circular(
|
||
16.rpx),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(
|
||
height: 240.rpx,
|
||
child: getOnePickers(
|
||
context,
|
||
list
|
||
.map((e) =>
|
||
"${e['name']}".tr+"(${e['id']})")
|
||
.toList(),
|
||
tempIndex,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
|
||
Future<void> showTimeZonePickerDialog(
|
||
BuildContext context, {
|
||
required String initialTimeZone, // 初始时区字符串,如 "UTC+8"
|
||
required Function(String selectedTimeZone) onConfirm,
|
||
String title = "选择时区",
|
||
}) async {
|
||
// 使用 AppConstants.integerTimeZones 作为数据源
|
||
List<String> timeZones = AppConstants.integerTimeZones;
|
||
int selectedIndex = timeZones.indexOf(initialTimeZone);
|
||
|
||
// 如果没有找到,使用默认的UTC+8(北京时间)
|
||
if (selectedIndex == -1) {
|
||
selectedIndex = timeZones.indexOf("UTC+8");
|
||
if (selectedIndex == -1) {
|
||
selectedIndex = 0; // 如果连UTC+8都没有,使用第一个
|
||
}
|
||
}
|
||
|
||
final RxInt tempIndex = RxInt(selectedIndex);
|
||
ThemeController themeController = Get.find();
|
||
|
||
await showDialog(
|
||
context: context,
|
||
barrierDismissible: true,
|
||
builder: (BuildContext context) {
|
||
return Stack(
|
||
children: [
|
||
Positioned(
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Dialog(
|
||
backgroundColor: themeController.currentColor.sc17,
|
||
insetPadding: EdgeInsets.zero,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(0),
|
||
),
|
||
child: Container(
|
||
padding: EdgeInsets.fromLTRB(0.rpx, 0.rpx, 0.rpx, 90.rpx),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Container(
|
||
padding:
|
||
EdgeInsets.fromLTRB(30.rpx, 0.rpx, 30.rpx, 0.rpx),
|
||
color: themeController.currentColor.sc5,
|
||
height: 80.rpx,
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.only(top: 0),
|
||
onTap: () {
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
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,
|
||
),
|
||
),
|
||
ClickableContainer(
|
||
backgroundColor: Colors.transparent,
|
||
highlightColor: Colors.transparent,
|
||
padding: EdgeInsets.only(top: 0),
|
||
onTap: () {
|
||
onConfirm(timeZones[tempIndex.value]);
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 110.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"确定".tr,
|
||
style: TextStyle(
|
||
fontSize: 30.rpx,
|
||
color: themeController.currentColor.sc2,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 20.rpx),
|
||
Stack(
|
||
children: [
|
||
Positioned.fill(
|
||
child: IgnorePointer(
|
||
child: Center(
|
||
child: Container(
|
||
height: 90.rpx,
|
||
margin:
|
||
EdgeInsets.symmetric(horizontal: 95.rpx),
|
||
decoration: BoxDecoration(
|
||
color: themeController.currentColor.sc2,
|
||
borderRadius: BorderRadius.circular(16.rpx),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(
|
||
height: 240.rpx,
|
||
child: getOnePickers(
|
||
context,
|
||
timeZones, // 传入字符串列表
|
||
tempIndex,
|
||
unit: "", // 时区不需要单位
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|