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 years = List.generate(100, (i) => DateTime.now().year - i) ..sort(); final List months = List.generate(12, (i) => i + 1); final List days = List.generate(31, (i) => i + 1); final RxList daysSelect = [].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: [ 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 years = List.generate(100, (i) => DateTime.now().year - i) ..sort(); final RxList months = [].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: [ // 顶部标题与按钮 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 showWeightPickerDialog( BuildContext context, { required String initialWeight, // 初始体重(单位:kg) required Function(int selectedWeight) onConfirm, String title = "选择体重", }) async { List 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 showHeightPickerDialog( BuildContext context, { required int initialHeight, // 初始身高(单位:cm) required Function(int selectedHeight) onConfirm, String title = "选择身高", }) async { List 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 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: [ // 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 dayTimeArr, Function(List)? checkChange, String title = "选择时间", }) { ThemeController themeController = Get.find(); Color checkColor = stringToColor("#D3B684"); final List hours = List.generate(24, (i) => i); final List 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: [ // 顶部栏:取消 - 标题 - 确定 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: [ // 顶部标题 + 按钮 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: [ 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: [ 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: [ ...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 progressNotifier, ValueNotifier 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( valueListenable: progressNotifier, builder: (context, progress, _) { return ValueListenableBuilder( 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: [ 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: [ 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), ), ), ), ) ], ) ], ), ), ); }, ); }