Files
tuiche/lib/pages/common/selectDialog.dart
2025-07-30 16:48:48 +08:00

1699 lines
65 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 'package:ef/ef.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/base/SleepCalendarWidget.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/controller/device/device_calibration_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
getOnePicker(BuildContext context, List arr, int checkIndex,
Function onSelectedItemChanged,
{bool looping = false, String unit = ''}) {
ThemeController themeController = Get.find();
return CupertinoPicker(
key: UniqueKey(),
useMagnifier: false,
itemExtent: 90.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) {
bool isSelected = index == checkIndex;
Color textColor = isSelected ? Color(0XFF011D33) : Color(0xFF9AA0B3);
return Container(
alignment: Alignment.center,
width: 400.rpx,
// decoration: BoxDecoration(
// border: Border(
// bottom: index != arr.length
// ? BorderSide(color: stringToColor("#8D95B0"))
// : BorderSide.none,
// ),
// ),
child: Text("${arr[index]}$unit",
style: TextStyle(
fontFamily: 'Readex Pro',
color: textColor,
letterSpacing: 0,
fontSize: 30.rpx)),
);
})
],
);
}
Widget getOnePickers(
BuildContext context,
List arr,
RxInt selectedIndex, {
String unit = '',
bool looping = false,
void Function(int)? onChanged,
bool isMonthName = false,
}) {
ThemeController themeController = Get.find();
final bool isEn = Get.locale?.languageCode.startsWith('en') ?? false;
return Obx(() {
return CupertinoPicker.builder(
itemExtent: 90.rpx,
useMagnifier: false,
magnification: 1,
diameterRatio: 3,
squeeze: 1,
scrollController:
FixedExtentScrollController(initialItem: selectedIndex.value),
selectionOverlay: Container(),
onSelectedItemChanged: (int index) {
selectedIndex.value = index;
if (onChanged != null) onChanged(index);
},
childCount: arr.length,
itemBuilder: (context, index) {
bool isSelected = index == selectedIndex.value;
// 处理显示文本
String displayText;
if (isMonthName && isEn && arr[index] is int) {
displayText = DateFormat.MMMM('en').format(DateTime(0, arr[index]));
} else {
displayText = isEn ? "${arr[index]}" : "${arr[index]}$unit"; // 中文附带单位
}
return Center(
child: Text(
displayText,
style: TextStyle(
fontFamily: 'Readex Pro',
color: isSelected
? const Color(0xFF011D33)
: const Color(0xFF9AA0B3),
fontSize: 30.rpx,
fontWeight: FontWeight.normal,
),
),
);
},
);
});
}
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: const Color(0xFF003058),
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: stringToColor("#84F5FF"))),
),
),
],
),
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: const Color(0xFF84F5FF),
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: "",
),
),
],
),
),
),
],
),
],
),
),
),
),
),
],
);
},
);
}
Future showMonthSelectionDialog(
BuildContext context, {
required DateTime checkDate,
Function(DateTime)? checkChange,
String title = "选择月份",
}) {
ThemeController themeController = Get.find();
final currentYear = DateTime.now().year;
final currentMonth = DateTime.now().month;
final List<int> years = List.generate(100, (i) => DateTime.now().year - i)
..sort();
final RxList<int> months = <int>[].obs;
// 初始化 yearIndex
final int initYearIndex = years.indexOf(checkDate.year);
final RxInt yearIndex = (initYearIndex >= 0 ? initYearIndex : 0).obs;
final RxInt monthIndex = 0.obs;
bool isInit = true;
void updateMonthList() {
final selectedYear = years[yearIndex.value];
final maxMonth = selectedYear == currentYear ? currentMonth : 12;
final newMonths = List.generate(maxMonth, (i) => i + 1);
months.value = newMonths;
if (isInit) {
final int newMonthIndex = newMonths.indexOf(checkDate.month);
monthIndex.value = newMonthIndex >= 0 ? newMonthIndex : 0;
isInit = false;
} else {
if (monthIndex.value >= newMonths.length) {
monthIndex.value = newMonths.length - 1;
}
}
}
updateMonthList();
ever(yearIndex, (_) => updateMonthList());
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: const Color(0xFF003058),
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],
);
Navigator.of(context).pop();
checkChange?.call(selectedDate);
},
child: Container(
width: 110.rpx,
height: 60.rpx,
alignment: Alignment.center,
child: Text(
"确定".tr,
style: TextStyle(
fontSize: 30.rpx,
color: stringToColor("#84F5FF"),
),
),
),
),
],
),
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: const Color(0xFF84F5FF),
borderRadius: BorderRadius.circular(16.rpx),
),
),
),
),
),
// 滚轮选择器
SizedBox(
height: 240.rpx,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 95.rpx),
child: Row(
children: [
Expanded(
child: getOnePickers(
context,
years,
yearIndex,
unit: "".tr,
),
),
Expanded(
child: getOnePickers(
context,
months, // 注意这里取 .value
monthIndex,
unit: "".tr,
),
),
],
),
),
),
],
),
],
),
),
),
),
),
],
);
},
);
}
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: Color(0xFF003058),
insetPadding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
child: Container(
padding: EdgeInsets.fromLTRB(30.rpx, 10.rpx, 30.rpx, 90.rpx),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 标题 + 按钮
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: stringToColor("#84F5FF"),
),
),
)),
],
),
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: Color(0xFF84F5FF),
borderRadius: BorderRadius.circular(16.rpx),
),
),
),
),
),
SizedBox(
height: 240.rpx,
child: getOnePickers(
context,
weights,
tempIndex, // ✅ 传入 RxInt
unit: "kg",
),
),
],
),
],
),
),
),
),
),
],
);
},
);
}
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: Color(0xFF003058),
insetPadding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
child: Container(
padding: EdgeInsets.fromLTRB(30.rpx, 10.rpx, 30.rpx, 90.rpx),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
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: stringToColor("#84F5FF"),
),
),
)),
],
),
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: Color(0xFF84F5FF),
borderRadius: BorderRadius.circular(16.rpx),
),
),
),
),
),
SizedBox(
height: 240.rpx,
child: getOnePickers(
context,
heights,
tempIndex, // ✅ 传入 RxInt
unit: "cm",
),
),
],
),
],
),
),
),
),
),
],
);
},
);
}
// Future showDayTimeSelectionDialog(BuildContext context,
// {required List<int> dayTimeArr, Function? checkChange, String title = ""}) {
// ThemeController themeController = Get.find();
// final hours = List.generate(24, (i) => i);
// final minutes = List.generate(60, (i) => i);
// final RxInt hoursIndex = RxInt(hours.indexOf(dayTimeArr[0]));
// final RxInt minutesIndex = RxInt(minutes.indexOf(dayTimeArr[1]));
// 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: stringToColor("#182B7C"),
// 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: [
// Text(
// title,
// style: FlutterFlowTheme.of(context)
// .bodyMedium
// .override(
// fontFamily: 'Readex Pro',
// color: themeController.currentColor.sc3,
// fontSize: 30.rpx,
// ),
// ),
// closeIconWhite,
// ],
// ),
// Container(
// height: 240.rpx,
// margin: EdgeInsets.only(top: 60.rpx, bottom: 60.rpx),
// padding: EdgeInsets.only(left: 30.rpx, right: 30.rpx),
// child: Row(
// children: [
// Expanded(
// child: Padding(
// padding:
// EdgeInsets.symmetric(horizontal: 10.rpx),
// child: getOnePickers(context, hours, hoursIndex,
// unit: ''),
// ),
// ),
// Text(
// "时",
// style: FlutterFlowTheme.of(context)
// .bodyMedium
// .override(
// fontFamily: 'Readex Pro',
// color: themeController.currentColor.sc3,
// fontSize: 30.rpx,
// ),
// ),
// Expanded(
// child: Padding(
// padding:
// EdgeInsets.symmetric(horizontal: 10.rpx),
// child: getOnePickers(
// context, minutes, minutesIndex,
// unit: ''),
// ),
// ),
// Text(
// "分",
// style: FlutterFlowTheme.of(context)
// .bodyMedium
// .override(
// fontFamily: 'Readex Pro',
// color: themeController.currentColor.sc3,
// fontSize: 30.rpx,
// ),
// ),
// ],
// ),
// ),
// InkWell(
// onTap: () {
// checkChange?.call([
// hours[hoursIndex.value],
// minutes[minutesIndex.value]
// ]);
// Get.back();
// },
// child: Container(
// height: 68.rpx,
// alignment: Alignment.center,
// decoration: BoxDecoration(
// color: stringToColor("#D3B684"),
// borderRadius: BorderRadius.circular(10.rpx),
// ),
// child: Text(
// "确定",
// style: FlutterFlowTheme.of(context)
// .bodyMedium
// .override(
// fontFamily: 'Readex Pro',
// color: themeController.currentColor.sc3,
// fontSize: 30.rpx,
// ),
// ),
// ),
// )
// ],
// ),
// ),
// ),
// ),
// ),
// ],
// );
// },
// );
// }
Future showDayTimeSelectionDialog(
BuildContext context, {
required List<int> dayTimeArr,
Function(List<int>)? checkChange,
String title = "选择时间",
}) {
ThemeController themeController = Get.find();
Color checkColor = stringToColor("#D3B684");
final List<int> hours = List.generate(24, (i) => i);
final List<int> minutes = List.generate(60, (i) => i);
final RxInt hoursIndex = hours.indexOf(dayTimeArr[0]).obs;
final RxInt minutesIndex = minutes.indexOf(dayTimeArr[1]).obs;
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: const Color(0xFF003058),
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.tr,
style: TextStyle(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
fontSize: 30.rpx,
),
),
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: Colors.transparent,
padding: EdgeInsets.zero,
onTap: () {
checkChange?.call([
hours[hoursIndex.value],
minutes[minutesIndex.value],
]);
Get.back();
},
child: Container(
width: 110.rpx,
height: 60.rpx,
alignment: Alignment.center,
child: Text("确定".tr,
style: TextStyle(
fontSize: 30.rpx,
color: stringToColor("#84F5FF"))),
),
),
],
),
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: const Color(0xFF84F5FF),
borderRadius: BorderRadius.circular(16.rpx),
),
),
),
),
),
SizedBox(
height: 240.rpx,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 95.rpx),
child: Row(
children: [
Expanded(
child: getOnePickers(
context,
hours,
hoursIndex,
unit: "".tr,
),
),
Expanded(
child: getOnePickers(
context,
minutes,
minutesIndex,
unit: "".tr,
),
),
],
),
),
),
],
),
],
),
),
),
),
),
],
);
},
);
}
Future showOneSelectionDialog(
BuildContext context, {
required List arr,
int checkIndex = 0,
Function? checkChange,
String title = "选择性别",
}) {
ThemeController themeController = Get.find();
Color checkColor = stringToColor("#D3B684");
final RxInt selectedIndex = checkIndex.obs; // ✅ 用RxInt变量监听选中项
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: const Color(0xFF003058),
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,
children: <Widget>[
// 顶部标题 + 按钮
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: Colors.transparent,
padding: EdgeInsets.zero,
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.tr,
style: TextStyle(
fontFamily: 'Readex Pro',
color: Colors.white,
fontSize: 30.rpx,
)),
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: Colors.transparent,
padding: EdgeInsets.zero,
onTap: () {
checkChange?.call(selectedIndex.value);
Get.back();
},
child: Container(
alignment: Alignment.center,
width: 110.rpx,
height: 60.rpx,
child: Text("确定".tr,
style: TextStyle(
fontSize: 30.rpx,
color: stringToColor("#84F5FF"))),
),
),
],
),
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: const Color(0xFF84F5FF),
borderRadius: BorderRadius.circular(16.rpx),
),
),
),
),
),
Container(
height: 240.rpx,
margin: EdgeInsets.only(
top: 60.rpx,
bottom: 60.rpx,
left: 30.rpx,
right: 30.rpx),
child: getOnePickers(
context,
arr,
selectedIndex,
unit: '',
),
),
],
),
],
),
),
),
),
),
],
);
},
);
}
enum ConfirmDialogIcon {
none,
danger,
success,
warn;
get uname {
String v = "";
switch (this) {
case ConfirmDialogIcon.danger:
v = "danger";
break;
case ConfirmDialogIcon.success:
v = "success";
break;
case ConfirmDialogIcon.warn:
v = "warn";
break;
case ConfirmDialogIcon.none:
v = "";
break;
}
return v;
}
}
Future showCustomConfirmDialog(BuildContext context, String name,
{String btnName = "确定",
ConfirmDialogIcon icon = ConfirmDialogIcon.warn}) async {
ThemeController themeController = Get.find();
return showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Container(
width: 660.rpx,
padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
alignment: Alignment.centerRight,
child: closeIcon,
),
SizedBox(height: 60.rpx),
if ("${icon.uname}".isNotEmpty)
Center(
child: Container(
margin: EdgeInsets.only(bottom: 39.rpx),
width: 50.rpx,
height: 50.rpx,
child: Image.asset("assets/images/toast/${icon.uname}.png"),
),
),
Center(
child: Text(
'${name}',
style: TextStyle(fontSize: 16),
),
),
SizedBox(height: 20.rpx),
Container(
margin: EdgeInsets.only(top: 50.rpx, bottom: 40.rpx),
alignment: Alignment.center,
child: InkWell(
onTap: () {
Get.back(result: "confirm");
},
child: Container(
width: 260.rpx,
height: 60.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: stringToColor("#D3B684")),
child: Text(
'$btnName',
style: TextStyle(
color: themeController.currentColor.sc3,
fontSize: 30.rpx),
),
),
),
)
],
),
),
);
},
);
}
Future showCustomConfirmAndCancelDialog(BuildContext context, String name,
{String confirmName = "确定",
String cancelName = "取消",
ConfirmDialogIcon icon = ConfirmDialogIcon.warn}) async {
ThemeController themeController = Get.find();
return showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Container(
width: 660.rpx,
padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
alignment: Alignment.centerRight,
child: closeIcon,
),
SizedBox(height: 40.rpx),
if ("${icon.uname}".isNotEmpty)
Center(
child: Container(
margin: EdgeInsets.only(bottom: 39.rpx),
width: 50.rpx,
height: 50.rpx,
child: Image.asset("assets/images/toast/${icon.uname}.png"),
),
),
Center(
child: Text(
'${name}',
style: TextStyle(fontSize: 16),
),
),
SizedBox(height: 20.rpx),
Container(
margin: EdgeInsets.only(top: 50.rpx, bottom: 40.rpx),
alignment: Alignment.center,
child: InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
Get.back(result: "cancel");
},
child: Container(
width: 200.rpx,
height: 60.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.black12)),
child: Text(
'$cancelName',
style:
TextStyle(color: Colors.black, fontSize: 30.rpx),
),
),
),
SizedBox(
width: 80.rpx,
),
InkWell(
onTap: () {
Get.back(result: "confirm");
},
child: Container(
width: 200.rpx,
height: 60.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: stringToColor("#D3B684")),
child: Text(
'$confirmName',
style: TextStyle(
color: themeController.currentColor.sc3,
fontSize: 30.rpx),
),
),
)
],
)),
)
],
),
),
);
},
);
}
//权限说明弹窗
void showPermissionInfoDialog(BuildContext context, List data) {
ThemeController themeController = Get.find();
showDialog(
context: context,
barrierDismissible: false, // 点击对话框外部可关闭
builder: (BuildContext context) {
return Stack(
children: [
Positioned(
top: 30.rpx, // 控制弹窗距离顶部的位置
left: 0,
right: 0,
child: Material(
color: Colors.transparent,
child: Dialog(
backgroundColor: themeController.currentColor.sc3,
insetPadding: EdgeInsets.fromLTRB(0, 0, 0, 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Container(
constraints: BoxConstraints(maxHeight: 500.rpx),
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
...List.generate(data.length, (index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${data[index][0]}",
style: TextStyle(
fontSize: 30.rpx,
color: stringToColor("#333333"),
),
),
SizedBox(
height: 4.rpx,
),
Text(
"${data[index][1]}",
style: TextStyle(
fontSize: 26.rpx,
color: stringToColor("#A4AABC"),
),
),
if (index != data.length - 1)
SizedBox(
height: 18.rpx,
),
],
);
}),
],
),
),
),
),
),
),
],
);
},
);
}
void showProgressDialog(
BuildContext context,
ValueNotifier<double> progressNotifier,
ValueNotifier<bool> failureNotifier,
) {
ThemeController themeController = Get.find();
DeviceCalibrationController deviceCalibrationController = Get.find();
showDialog(
context: context,
barrierDismissible: false, // 点击对话框外部不可关闭
builder: (BuildContext dialogContext) {
return Stack(
children: [
Positioned(
top: MediaQuery.of(context).size.height * 0.4,
left: MediaQuery.of(context).size.width * 0.05,
right: MediaQuery.of(context).size.width * 0.05,
child: Material(
color: Colors.transparent,
child: Dialog(
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: ValueListenableBuilder<double>(
valueListenable: progressNotifier,
builder: (context, progress, _) {
return ValueListenableBuilder<bool>(
valueListenable: failureNotifier,
builder: (context, isFailure, __) {
// 关闭弹窗的逻辑
if (isFailure) {
// 延迟关闭弹窗和提示错误
Future.delayed(Duration(milliseconds: 300), () {
if (Navigator.canPop(dialogContext)) {
Navigator.of(dialogContext).pop();
}
});
} else if (progress >= 100) {
// 延迟关闭弹窗和提示成功(可选)
Future.delayed(Duration(milliseconds: 300), () {
if (Navigator.canPop(dialogContext)) {
Navigator.of(dialogContext).pop();
}
});
}
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
isFailure
? '失败'.tr
: '${(progress).toStringAsFixed(0)}%',
style: TextStyle(
fontSize: 26.rpx,
color: isFailure
? Colors.red
: themeController.currentColor.sc3,
),
),
SizedBox(height: 40.rpx),
Stack(
children: [
Container(
width: double.infinity,
height: 21.rpx,
decoration: BoxDecoration(
color: stringToColor("#D9D9D9"),
borderRadius: BorderRadius.circular(
AppConstants().button_container_radius,
),
),
),
Container(
width: progress /
100 *
MediaQuery.of(context).size.width *
0.8,
height: 21.rpx,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: isFailure
? [themeController.currentColor.sc9]
: [
themeController
.currentColor.sc2,
themeController
.currentColor.sc2,
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
borderRadius: BorderRadius.circular(
AppConstants().button_container_radius,
),
),
),
],
),
],
);
},
);
},
),
),
),
),
),
],
);
},
);
}
void showSleepCalendarBottomSheet({
required BuildContext context,
int? timestamp,
int? type = 1,
String? mac,
required void Function(DateTime selectedDate) onDateSelected,
}) {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: 'Dismiss',
barrierColor: Colors.black.withOpacity(0.4), // 移到这里,替代 Scaffold 背景
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation) {
return GestureDetector(
onTap: () {
Navigator.of(context).pop(); // 点击空白关闭
},
child: Material(
type: MaterialType.transparency,
child: Align(
alignment: Alignment.bottomCenter,
child: GestureDetector(
onTap: () {}, // 阻止点击透传到外部(避免误关)
child: FractionallySizedBox(
widthFactor: 1.0,
heightFactor: 0.55,
child: Container(
decoration: BoxDecoration(
color: const Color(0xFF242835),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.rpx),
topRight: Radius.circular(20.rpx),
),
),
child: SleepCalendarWidget(
timestamp: timestamp,
type: type,
mac: mac,
onDateSelected: onDateSelected,
),
),
),
),
),
),
);
},
);
}
Future showCustomConfirmOfWebViewDialog(
BuildContext context, String name, String webviewUrl,
{String btnName = "确定",
bool showCancel = false,
String cancelName = "取消",
ConfirmDialogIcon icon = ConfirmDialogIcon.warn}) async {
return showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return Dialog(
insetPadding: EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Container(
width: 640.rpx,
padding: EdgeInsets.fromLTRB(22.rpx, 0, 20.rpx, 10.rpx),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
alignment: Alignment.centerRight,
child: closeIcon,
),
SizedBox(height: 40.rpx),
// Container(
// height: MediaQuery.of(context).size.height * 0.4,
// child: Shopping(
// url: webviewUrl,
// ),
// ),
SizedBox(height: 20.rpx),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (showCancel)
Container(
margin: EdgeInsets.only(
top: 50.rpx, bottom: 40.rpx, right: 100.rpx),
alignment: Alignment.center,
child: InkWell(
onTap: () {
Get.back();
},
child: Container(
width: 200.rpx,
height: 60.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.black12)),
child: Text(
'$cancelName',
style: TextStyle(
color: Colors.black, fontSize: 30.rpx),
),
),
),
),
Container(
margin: EdgeInsets.only(top: 50.rpx, bottom: 40.rpx),
alignment: Alignment.center,
child: InkWell(
onTap: () {
Get.back(result: "confirm");
},
child: Container(
width: 200.rpx,
height: 60.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: stringToColor("#D3B684")),
child: Text(
'$btnName',
style:
TextStyle(color: Colors.white, fontSize: 30.rpx),
),
),
),
)
],
)
],
),
),
);
},
);
}