1471 lines
56 KiB
Dart
1471 lines
56 KiB
Dart
import 'package:ef/ef.dart';
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter/material.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)),
|
||
);
|
||
})
|
||
],
|
||
);
|
||
}
|
||
|
||
//支持选中时文字变色
|
||
getOnePickers(
|
||
BuildContext context,
|
||
List arr,
|
||
RxInt selectedIndex, {
|
||
String unit = '',
|
||
bool looping = false,
|
||
void Function(int)? onChanged,
|
||
}) {
|
||
ThemeController themeController = Get.find();
|
||
|
||
return Obx(() {
|
||
return CupertinoPicker.builder(
|
||
itemExtent: 90.rpx,
|
||
useMagnifier: false,
|
||
magnification: 1,
|
||
diameterRatio: 3,
|
||
squeeze: 1,
|
||
// looping: looping,
|
||
scrollController:
|
||
FixedExtentScrollController(initialItem: selectedIndex.value),
|
||
selectionOverlay: Container(),
|
||
onSelectedItemChanged: (int index) {
|
||
selectedIndex.value = index;
|
||
},
|
||
childCount: arr.length,
|
||
itemBuilder: (context, index) {
|
||
bool isSelected = index == selectedIndex.value;
|
||
return Center(
|
||
child: Text(
|
||
"${arr[index]}$unit",
|
||
style: TextStyle(
|
||
fontFamily: 'Readex Pro',
|
||
color: isSelected
|
||
? const Color(0xFF011D33) // ✅ 选中项颜色
|
||
: Color(0xFF9AA0B3), // 未选中颜色
|
||
fontSize: 30.rpx,
|
||
fontWeight: FontWeight.normal,
|
||
),
|
||
),
|
||
);
|
||
},
|
||
);
|
||
});
|
||
}
|
||
|
||
Future showDateSelectionDialog(BuildContext context,
|
||
{required DateTime checkDate,
|
||
Function? checkChange,
|
||
String title = "选择生日"}) {
|
||
ThemeController themeController = Get.find();
|
||
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: 100.rpx,
|
||
height: 60.rpx,
|
||
alignment: Alignment.center,
|
||
child: Text("取消",
|
||
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: 100.rpx,
|
||
height: 60.rpx,
|
||
alignment: Alignment.center,
|
||
child: Text("确定",
|
||
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: "年",
|
||
onChanged: (_) => updateDays(),
|
||
),
|
||
),
|
||
Expanded(
|
||
child: getOnePickers(
|
||
context,
|
||
months,
|
||
monthIndex,
|
||
unit: "月",
|
||
onChanged: (_) => updateDays(),
|
||
),
|
||
),
|
||
Expanded(
|
||
child: getOnePickers(
|
||
context,
|
||
daysSelect,
|
||
dayIndex,
|
||
unit: "日",
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
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: 100.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"取消",
|
||
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(
|
||
weights[tempIndex.value]); // ✅ 回调返回选中值
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
width: 100.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"确定",
|
||
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: 100.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"取消",
|
||
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: 100.rpx,
|
||
height: 60.rpx,
|
||
child: Text(
|
||
"确定",
|
||
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: 100.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: 100.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: 100.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: 100.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),
|
||
),
|
||
),
|
||
),
|
||
)
|
||
],
|
||
)
|
||
],
|
||
),
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|