import 'dart:async'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:vbvs_app/common/color/app_uri_status.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/controller/setting/language/language_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/language/AppLanguage.dart'; import 'package:vbvs_app/model/api_response.dart'; ThemeController themeController = Get.find(); LanguageController languageController = Get.find(); class MyUtils { static String formatDate(DateTime dateTime) { return "${dateTime.year}-${dateTime.month}-${dateTime.day.toString().padLeft(2, '0')}"; } static String formatToDate(int timestamp) { final dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp); return "${dateTime.year}-${dateTime.month}-${dateTime.day.toString().padLeft(2, '0')}"; } static Map diffHoursMinutesMap(int startMillis, int endMillis) { final duration = Duration(milliseconds: endMillis - startMillis); final hours = duration.inHours; final minutes = duration.inMinutes % 60; return { "hours": hours, "minutes": minutes, }; } static String formatToHHmm(int timestampMillis) { final dateTime = DateTime.fromMillisecondsSinceEpoch(timestampMillis); final twoDigits = (int n) => n.toString().padLeft(2, '0'); return '${twoDigits(dateTime.hour)}:${twoDigits(dateTime.minute)}'; } static ApiResponse formatResponse( ApiResponse res, String successMsg, String errorMsg, ) { if (res.code == HttpStatusCodes.ok) { // 成功但 msg 为空时填默认成功提示 if (res.msg == null || res.msg!.isEmpty) { res.msg = successMsg; } } else { // 失败且 msg 为空时填默认失败提示 if (res.msg == null || res.msg!.isEmpty) { res.msg = errorMsg; } } return res; } static String timestampToDateString(int timestamp) { DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp); String formattedDate = '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} ' '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}:${dateTime.second.toString().padLeft(2, '0')}'; return formattedDate; } static String hidePhoneNumber(String phoneNumber) { if (phoneNumber.length != 11) { // 检查手机号是否为11位 throw Exception("手机号格式不正确"); } // 将中间四位替换为星号 return phoneNumber.replaceRange(3, 7, '****'); } static double initialScrollOffset = 0.0; // 判断手机号格式是否正确的方法 static bool isValidPhoneNumber(String phoneNumber) { final RegExp phoneRegExp = RegExp(r'^1[3-9]\d{9}$'); return phoneRegExp.hasMatch(phoneNumber); } static bool isValidEmail(String email) { final RegExp emailRegExp = RegExp( r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', ); return emailRegExp.hasMatch(email); } static Future makePhoneCall(String phoneNumber) async { final Uri launchUri = Uri( scheme: 'tel', path: phoneNumber, ); if (await canLaunchUrl(launchUri)) { await launchUrl(launchUri); } else { throw '无法拨打电话'; } } static String formatDateTime(DateTime dateTime) { // 定义日期格式 // dateTime.toLocal(); final DateFormat formatter = DateFormat('yyyy-MM-dd HH:mm'); // 返回格式化后的字符串 return formatter.format(dateTime); } static void scrollToFocusedInput(FocusNode focusNode, _scrollController) { // 获取输入框相对于整个页面的偏移量 RenderObject? object = focusNode.context?.findRenderObject(); if (object != null) { final RenderBox box = object as RenderBox; final Offset position = box.localToGlobal(Offset.zero); // 将页面滚动到使输入框显示在屏幕上的位置 _scrollController.animateTo( position.dy - MediaQuery.of(focusNode.context!).size.height / 4, duration: Duration(milliseconds: 300), curve: Curves.easeInOut, ); } } static void resetScrollPosition(_scrollController) { _scrollController.animateTo( initialScrollOffset, duration: Duration(milliseconds: 300), curve: Curves.easeInOut, ); } static String formatBindTime(DateTime d) { final DateFormat formatter = DateFormat('yyyy/MM/dd'); return formatter.format(d); } static DateTime? formatBirthdayTime(String? device) { if (device == null || device.isEmpty) return null; try { return DateTime.parse(device.replaceAll('/', '-')); // 替换为标准格式 } catch (e) { return null; // 解析失败时返回 null } } static int getAgeByDate(DateTime? formatBirthdayTime) { if (formatBirthdayTime == null) return 0; final now = DateTime.now(); int age = now.year - formatBirthdayTime.year; // 如果还没到今年生日,减一岁 if (now.month < formatBirthdayTime.month || (now.month == formatBirthdayTime.month && now.day < formatBirthdayTime.day)) { age--; } return age; } static String formatDateTimeWeek(DateTime date) { DateTime now = DateTime.now(); // 去除时间部分,仅比较年月日 DateTime today = DateTime(now.year, now.month, now.day); DateTime target = DateTime(date.year, date.month, date.day); if (target == today) { return '今日'.tr; } List weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; String currentLanguageCode = AppLanguage().getCurrentLanguageCode(); if (currentLanguageCode != null) { if (currentLanguageCode != "zh_CN") { weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; } } return weekdays[date.weekday % 7]; // Dart中星期日是7,要映射到索引0 } /// 返回 MM/dd 格式 static String formatDateTimeDay(DateTime date) { String twoDigits(int n) => n.toString().padLeft(2, '0'); return '${twoDigits(date.month)}/${twoDigits(date.day)}'; } static String getFormatChineseTime(int date, {bool showWeekday = true}) { DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(date); // 格式化年月日 String dateStr = DateFormat('yyyy年MM月dd日').format(dateTime); if (!showWeekday) return dateStr; // 获取星期 const weekDays = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']; String weekStr = weekDays[dateTime.weekday - 1]; return '$dateStr $weekStr'; } static String getFormatEnglishDate(int millis) { final date = DateTime.fromMillisecondsSinceEpoch(millis); const weekdays = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]; final weekday = weekdays[date.weekday - 1]; final formattedDate = '${date.year}/${date.month.toString().padLeft(2, '0')}/${date.day.toString().padLeft(2, '0')}'; return '$weekday, $formattedDate'; } } Color stringToColor(String hexColor) { String formattedColor = hexColor.replaceAll("#", ""); int colorValue = int.parse("0xFF$formattedColor"); return Color(colorValue); } //字节转16进制 String ab2str(List buffer) { return buffer.map((x) => x.toRadixString(16).padLeft(2, '0')).join(''); } enum ToastColor { success, error, warn } ToastColor color_success = ToastColor.success; ToastColor color_warning = ToastColor.warn; ToastColor color_error = ToastColor.error; showToast(String msg, {ToastColor color = ToastColor.error, int closeTime = 3}) { // Fluttertoast.showToast( // msg: msg, // toastLength: Toast.LENGTH_LONG, // gravity: ToastGravity.CENTER, // timeInSecForIosWeb: 1, // backgroundColor: color == null ? color_error : color, // fontSize: 14.0, // ); final context = Get.overlayContext; // 获取 Overlay 的上下文 Color background = Colors.red; Color color_text = stringToColor("#FA5A4C"); String icon = ""; if (color == color_success) { background = stringToColor("#DBF1E1"); icon = "success"; color_text = stringToColor("#31CA79"); } else if (color == color_warning) { background = stringToColor("#FDF6EC"); icon = "warn"; color_text = stringToColor("#F8AE00"); } else if (color == color_error) { background = stringToColor("#FEF0F0"); icon = "error"; color_text = stringToColor("#FA5A4C"); } if (context == null) return; OverlayEntry overlayEntry = OverlayEntry( builder: (context) => Positioned( top: MediaQuery.of(context).size.height * 0.10, left: MediaQuery.of(context).size.width * 0.5 - 225.rpx, child: Material( color: Colors.transparent, child: Container( width: 450.rpx, padding: EdgeInsets.only(top: 20.rpx, bottom: 20.rpx), decoration: BoxDecoration( color: background, borderRadius: BorderRadius.circular(10.rpx), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "assets/images/toast/${icon}_.png", width: 30.rpx, height: 30.rpx, ), SizedBox( width: 18.rpx, ), Container( constraints: BoxConstraints(maxWidth: 380.rpx), child: Text( msg, maxLines: 9, style: TextStyle( color: color_text, fontSize: 28.rpx, fontWeight: FontWeight.w400), ), ) ], ), ), ), ), ); Overlay.of(context)?.insert(overlayEntry); Future.delayed(Duration(seconds: closeTime), () { overlayEntry.remove(); }); } var closeIcon = GestureDetector( onTapDown: (f) { Get.back(); }, child: Container( padding: EdgeInsets.fromLTRB(10.rpx, 18.rpx, 10.rpx, 10.rpx), child: Container( height: 42.rpx, width: 42.rpx, alignment: Alignment.center, child: Icon( Icons.close, ), ), ), ); var closeIconWhite = GestureDetector( onTapDown: (f) { Get.back(); }, child: Container( padding: EdgeInsets.all(10.rpx), child: Container( height: 42.rpx, width: 42.rpx, alignment: Alignment.center, child: Icon( Icons.close, color: themeController.currentColor.sc3, ), ), ), ); var returnIconButtom = IconButton( // padding: EdgeInsets.zero, // 去除默认 padding // constraints: BoxConstraints(), // 去除最小尺寸限制 onPressed: () => Get.back(), icon: Icon(Icons.navigate_before, size: 60.rpx), ); var returnIconButtomAddCallback = ( VoidCallback? returnCallBack, { bool enableBack = true, }) { return IconButton( onPressed: () { returnCallBack?.call(); if (enableBack) { Get.back(); } }, icon: Icon(Icons.navigate_before, size: 60.rpx), ); }; String time_08_Formatter(String time) { if (time == null || time == "") { return ""; } return DateFormat("yyyy-MM-dd HH:mm:ss") .format(DateTime.parse(time).toLocal()); } String time_08_Formatter_pattern(String time, String pattern) { if (time == null || time == "") { return ""; } return DateFormat(pattern).format(DateTime.parse(time).toLocal()); } enum LoadingDialogIcon { ble, wifi, none } class LoadingDialog { static show(String name, {LoadingDialogIcon icon = LoadingDialogIcon.none}) { ThemeController themeController = Get.find(); String iconUrl = ""; if (icon == LoadingDialogIcon.wifi) { iconUrl = "wifi"; } else if (icon == LoadingDialogIcon.ble) { iconUrl = "ble"; } Get.dialog( PopScope( canPop: false, child: Container( // color: Colors.transparent, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( constraints: BoxConstraints(minWidth: 300.rpx, maxWidth: 500.rpx), decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ if (iconUrl.isNotEmpty) Container( width: 120.rpx, height: 120.rpx, margin: EdgeInsets.only(bottom: 60.rpx), child: Image.asset("assets/images/toast/${iconUrl}.png"), ), Text( textAlign: TextAlign.center, name, style: TextStyle( fontSize: 16, color: themeController.currentColor.sc3, decoration: TextDecoration.none), ), SizedBox( height: 30.rpx, ), CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( themeController.currentColor.sc3, ), ), ], ), ) ], ), ), ), barrierDismissible: false, barrierColor: Color.fromRGBO(0, 0, 0, 0.8)); } static hide() { Get.back(); } }