diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 17f8265..7769da3 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-ldpi/ic_launcher.png b/android/app/src/main/res/mipmap-ldpi/ic_launcher.png index e9d9fbc..36ff3e4 100644 Binary files a/android/app/src/main/res/mipmap-ldpi/ic_launcher.png and b/android/app/src/main/res/mipmap-ldpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 98c4f5e..ff2ab98 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 63ce850..6a02963 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 92662bd..da91305 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index d31b2cb..3475f3f 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/assets/img/google.png b/assets/img/google.png new file mode 100644 index 0000000..eecdf84 Binary files /dev/null and b/assets/img/google.png differ diff --git a/assets/img/help_op.png b/assets/img/help_op.png new file mode 100644 index 0000000..0b0317f Binary files /dev/null and b/assets/img/help_op.png differ diff --git a/assets/img/icon/add.svg b/assets/img/icon/add.svg new file mode 100644 index 0000000..3872b5c --- /dev/null +++ b/assets/img/icon/add.svg @@ -0,0 +1 @@ +资源 275 \ No newline at end of file diff --git a/assets/img/icon/arrow_left.svg b/assets/img/icon/arrow_left.svg new file mode 100644 index 0000000..0e389d4 --- /dev/null +++ b/assets/img/icon/arrow_left.svg @@ -0,0 +1 @@ +资源 261 \ No newline at end of file diff --git a/assets/img/icon/bluetooth.svg b/assets/img/icon/bluetooth.svg new file mode 100644 index 0000000..49752b7 --- /dev/null +++ b/assets/img/icon/bluetooth.svg @@ -0,0 +1 @@ +资源 262 \ No newline at end of file diff --git a/assets/img/icon/close.svg b/assets/img/icon/close.svg new file mode 100644 index 0000000..ee25aa2 --- /dev/null +++ b/assets/img/icon/close.svg @@ -0,0 +1 @@ +资源 231 \ No newline at end of file diff --git a/assets/img/icon/query.svg b/assets/img/icon/query.svg new file mode 100644 index 0000000..90aa229 --- /dev/null +++ b/assets/img/icon/query.svg @@ -0,0 +1 @@ +资源 233 \ No newline at end of file diff --git a/assets/img/icon/scan.svg b/assets/img/icon/scan.svg new file mode 100644 index 0000000..6e38491 --- /dev/null +++ b/assets/img/icon/scan.svg @@ -0,0 +1 @@ +资源 263 \ No newline at end of file diff --git a/assets/img/icon/sound.svg b/assets/img/icon/sound.svg new file mode 100644 index 0000000..fcec23f --- /dev/null +++ b/assets/img/icon/sound.svg @@ -0,0 +1 @@ +资源 234 \ No newline at end of file diff --git a/assets/img/icon/tick.svg b/assets/img/icon/tick.svg new file mode 100644 index 0000000..dbd8ee1 --- /dev/null +++ b/assets/img/icon/tick.svg @@ -0,0 +1 @@ +资源 235 \ No newline at end of file diff --git a/assets/img/man.png b/assets/img/man.png new file mode 100644 index 0000000..606e5bb Binary files /dev/null and b/assets/img/man.png differ diff --git a/assets/img/mye.png b/assets/img/mye.png new file mode 100644 index 0000000..8987727 Binary files /dev/null and b/assets/img/mye.png differ diff --git a/assets/img/netlove.png b/assets/img/netlove.png new file mode 100644 index 0000000..6d259d1 Binary files /dev/null and b/assets/img/netlove.png differ diff --git a/assets/img/tel.png b/assets/img/tel.png new file mode 100644 index 0000000..361e837 Binary files /dev/null and b/assets/img/tel.png differ diff --git a/assets/img/wechat.png b/assets/img/wechat.png new file mode 100644 index 0000000..fd454ee Binary files /dev/null and b/assets/img/wechat.png differ diff --git a/assets/img/woman.png b/assets/img/woman.png new file mode 100644 index 0000000..bd7ba9c Binary files /dev/null and b/assets/img/woman.png differ diff --git a/assets/langs/zh_CN.json b/assets/langs/zh_CN.json index 3d00d81..257469f 100644 --- a/assets/langs/zh_CN.json +++ b/assets/langs/zh_CN.json @@ -14,7 +14,10 @@ "提示内容3": "3.若使用扫一扫功能,请对摄像头进行授权。", "扫一扫绑定": "扫一扫添加新设备", "蓝牙绑定": "蓝牙搜附近的设备", - "已关联体征监测设备": "已关联体征监测设备" + "已关联体征监测设备": "已关联体征监测设备", + "我的e护": "我的e护", + "云关爱": "云关爱", + "报告详情": "报告详情" }, "我的": { "个人信息": "个人信息", @@ -45,7 +48,30 @@ "信号":"最小信号强度", "搜索提示":"检索设备", "搜索":"搜索", - "匹配":"匹配出的外围设备" + "匹配":"匹配出的外围设备", + "信号强度":"信号强度", + "SN":"SN", + "蓝牙地址":"蓝牙地址", + "mac":"mac", + "网络":"网络", + "在线":"在线", + "离线":"离线", + "版本":"版本", + "默认设备名称":"未知设备", + "传感器":"传感器", + "可绑定":"可绑定", + "已被绑定":"已被绑定", + "双人版绑定标题":"该设备为双人版,请选择", + "绑定全部":"绑定全部", + "主设备":"主设备:", + "从设备":"从设备:", + "确定":"确定", + "取消":"取消", + "无法绑定":"无法绑定!", + "无法绑定1":"检测到该设备", + "无法绑定2":"已被绑定", + "无法绑定3":",绑定前请先进行解绑,有疑问请联系客服", + "知道了":"知道了" }, "登录页":{ "欢迎使用太和e护":"欢迎使用太和e护", @@ -76,6 +102,29 @@ "分享内容":"设备绑定成功后,如需对朋友或家人共享我的睡眠情况,可以进行立即分享,分享成功后,对方即可享受查看该设备权限,可以收到该设备的睡眠报告。", "立即分享":"立即分享", "返回":"返回首页 开启体验" + }, + "日期":{ + "取消":"取消", + "确定":"确定", + "年":"年", + "月":"月", + "日":"日" + }, + "wifi页":{ + "标题":"WIFI配置", + "跳过":"跳过", + "WLAN":"WLAN", + "未连接":"未连接", + "已连接":"已连接", + "可用WLAN":"可用WLAN", + "刷新":"刷新" + }, + "其他手机登录页":{ + "输入内容":"输入手机号码/邮箱", + "输入验证码":"输入验证码", + "获取验证码":"获取验证码", + "登录":"登录" + } } \ No newline at end of file diff --git a/lib/common/color/appConstants.dart b/lib/common/color/appConstants.dart index 49142ed..5cd8c8c 100644 --- a/lib/common/color/appConstants.dart +++ b/lib/common/color/appConstants.dart @@ -19,6 +19,7 @@ class AppConstants { double text_padding_up_dowm_p = 5.rpx; //段落文字上下间距 + double small_text_fontSize = 20.rpx; //普通文字字号 double normal_text_fontSize = 26.rpx; //普通文字字号 double title_text_fontSize = 30.rpx; //标题文字字号 } diff --git a/lib/common/util/Ble.dart b/lib/common/util/Ble.dart new file mode 100644 index 0000000..a1c02c4 --- /dev/null +++ b/lib/common/util/Ble.dart @@ -0,0 +1,1100 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:ef/ef.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/pages/common/selectDialog.dart'; + +String findInput = ""; +double rssichange = -90; +Function? findCall; + +int mcuMax = 500; + +String myuuid = "00000001-0000-1000-8000-00805F9B34FB"; + +int closeTime = 20000; + +Map devices = Map(); + +StreamSubscription? subscription_adapterState; + +Map connectList = + Map(); + +bool isBleStart = false; + +StreamSubscription>? onScanResultsListen; + +StreamSubscription? + streamSubscription_onConnectionStateChangedEvent; + +Timer? showToastTimer; + +List permissionInfo = [ + ["位置权限说明", "获得位置信息,连接附近的蓝牙设备与推荐附近门店"], + ["蓝牙权限说明", "搜索链接附近的蓝牙设备"], + ["附近设备权限说明", "搜索链接附近的蓝牙设备"] +]; + +bool isQuanShiDevice(name) { + return "$name".contains("S4-ZM-M94-4") || "$name".contains("S4-ZM-N94-4") || "$name".contains("MHT-SWES-D"); +} + +bool isMHTSWES(name) { + return "$name".contains("MHT-SWES-H") || "$name".contains("MHT-SWES-M") || "$name".contains("MHT-SWES-S"); +} + +bleParse() { + findCall = null; + print("bleParse 执行了"); + showToastTimer?.cancel(); + if (FlutterBluePlus.isScanningNow) { + FlutterBluePlus.stopScan(); + } +} + +start(Function fun, {Function? bleOnCall}) async { + if (isBleStart) { + var isOk = await requestBluetoothPermission(); + if (isOk == true) { + findCall = fun; + bleOnCall?.call(); + bleOnCall = null; + } + print("ble start again"); + return; + } + print("ble start"); + FlutterBluePlus.setLogLevel(LogLevel.info, color: false); + streamSubscription_onConnectionStateChangedEvent?.cancel(); + streamSubscription_onConnectionStateChangedEvent = + FlutterBluePlus.events.onConnectionStateChanged.listen((event) { + print('${event.device} ${event.connectionState}'); + }); + if (await FlutterBluePlus.isSupported == false) { + print("Bluetooth not supported by this device"); + return; + } + + // handle bluetooth on & off + // note: for iOS the initial state is typically BluetoothAdapterState.unknown + // note: if you have permissions issues you will get stuck at BluetoothAdapterState.unauthorized + subscription_adapterState?.cancel(); + int callIndex = 0; + subscription_adapterState = + FlutterBluePlus.adapterState.listen((BluetoothAdapterState state) async { + print(state); + print("蓝牙状态 $state"); + if (state == BluetoothAdapterState.on) { + showToastTimer?.cancel(); + // usually start scanning, connecting, etc + findCall = fun; + var isOk = await requestBluetoothPermission(); + if (isOk == true) { + bleOnCall?.call(); + bleOnCall = null; + } + } else { + // show an error to the user, etc + if (Platform.isIOS && + callIndex == 0 && + state == BluetoothAdapterState.unknown) { + callIndex++; + return; + } + if (Platform.isAndroid) { + showToast("请打开蓝牙开关"); + } + if (Platform.isIOS) { + showToast("请打开蓝牙开关且开启蓝牙权限"); + await showCustomConfirmAndCancelDialog( + Get.context!, "请在“设置-蓝牙”中打开蓝牙开关或者在“设置-APP”中找到对应APP开启蓝牙权限", + confirmName: "去设置") + .then((msg) async { + if (msg == "confirm") { + openAppSettings(); + } + }); + } + isBleStart = false; + showToastTimer?.cancel(); + showToastTimer = Timer.periodic(const Duration(seconds: 5), (t) { + if (t.tick > 3) { + t.cancel(); + } + if (Platform.isAndroid) { + showToast("请打开蓝牙开关"); + } + if (Platform.isIOS) { + showToast("请打开蓝牙开关且开启蓝牙权限"); + } + }); + } + callIndex++; + }); + + // turn on bluetooth ourself if we can + // for iOS, the user controls bluetooth enable/disable + if (Platform.isAndroid && + FlutterBluePlus.adapterStateNow != BluetoothAdapterState.on) { + showPermissionInfoDialog(Get.context!, permissionInfo); + FlutterBluePlus.turnOn().then((e) { + Get.back(); + }).catchError((e) { + Get.back(); + }); + } + + var timer = null; + onScanResultsListen?.cancel(); + onScanResultsListen = FlutterBluePlus.onScanResults.listen( + (List results) { + // print(results.length); + for (ScanResult result in results) { + // if (result.device.id.toString().contains("A3:76")) { + // print("$result"); + // } + Map d = { + "updateTime": DateTime.now().millisecondsSinceEpoch, + "name": result.device.advName, + "id": result.device.remoteId.str, + "rssi": result.rssi, + "device": result.device, + "connectable": result.advertisementData.connectable + }; + Map> m_d = result.advertisementData.manufacturerData; + m_d.keys.toList().forEach((v) { + if (v == 65517 && m_d[65517]?.length != 0) { + List a = [0, 0, ...?m_d[65517]]; + advertisDataFormatter(a, d); + } else if (v == 11125 && m_d[11125]?.length == 8) { + List a = [...?m_d[11125]]; + d['adData'] = {'deviceId': ab2str(a.sublist(2, 8)).toUpperCase()}; + } else if (m_d[v]?.length == 8 && isQuanShiDevice(d["name"])) { + List a = [...?m_d[v]]; + d['adData'] = {'deviceId': ab2str(a.sublist(2, 8)).toUpperCase()}; + } else if (m_d[v]?.length == 4 && isMHTSWES(d["name"])) { + ByteData bd = ByteData(2); + bd.setUint16(0, v, Endian.little); + List a = [bd.getUint8(0), bd.getUint8(1), ...?m_d[v]]; + d['adData'] = {'deviceId': ab2str(a).toUpperCase()}; + } else if (m_d[v]?.length == 6 && isMHTSWES(d["name"])) { + List a = [...?m_d[v]]; + d['adData'] = {'deviceId': ab2str(a).toUpperCase()}; + } + }); + devices[d['id']] = d; + // print('Device found: ${result.device.name}, ${result.device.id}'); + if (timer == null) { + timer = 1; + timer = Future.delayed(const Duration(microseconds: 300), () { + timer = null; + find(); + }); + } + } + }, + onError: (e) => print(e), + ); +} + +// Future locationCheck({bool isGetLocation = true}) async { +// // 先查看定位服务是否开启 +// bool b = await Geolocator.isLocationServiceEnabled(); +// if (b == false) { +// if (Platform.isAndroid) { +// showToast("请开启系统位置开关", closeTime: 5); +// await showCustomConfirmAndCancelDialog(Get.context!, "请开启系统位置开关", +// confirmName: "去设置") +// .then((msg) async { +// if (msg == "confirm") { +// // await Geolocator.openLocationSettings(); +// await openGeolocatorLocationSettingsAndWait(); +// b = await Geolocator.isLocationServiceEnabled(); +// } +// }); +// } +// if (Platform.isIOS) { +// showToast("请开启系统定位服务与定位权限", closeTime: 5); +// await showCustomConfirmAndCancelDialog(Get.context!, +// "请在“设置-隐私与安全性-定位服务”中开启定位服务开关或者在“设置-APP”中找到对应APP开启定位服务权限", +// confirmName: "去设置") +// .then((msg) async { +// if (msg == "confirm") { +// // await Geolocator.openAppSettings(); +// await openGeolocatorAppSettingsAndWait(); +// b = await Geolocator.isLocationServiceEnabled(); +// } +// }); +// } +// } + +// var permission = await Geolocator.checkPermission(); +// if (permission == LocationPermission.denied || +// permission == LocationPermission.deniedForever) { +// showPermissionInfoDialog(Get.context!, [permissionInfo[0]]); +// permission = await Geolocator.requestPermission().catchError((e) { +// Get.back(); +// }); +// Get.back(); +// if (permission == LocationPermission.deniedForever) { +// if (Platform.isAndroid) { +// await showCustomConfirmAndCancelDialog(Get.context!, "请开启位置权限(打开精确位置)", +// confirmName: "去设置") +// .then((msg) async { +// if (msg == "confirm") { +// // await Geolocator.openAppSettings(); +// await openGeolocatorAppSettingsAndWait(); +// } +// }); +// } +// if (Platform.isIOS) { +// await showCustomConfirmAndCancelDialog(Get.context!, +// "请在“设置-隐私与安全性-定位服务”中开启定位服务开关或者在“设置-APP”中找到对应APP开启定位服务权限", +// confirmName: "去设置") +// .then((msg) async { +// if (msg == "confirm") { +// // await Geolocator.openAppSettings(); +// await openGeolocatorAppSettingsAndWait(); +// } +// }); +// } +// } +// } + +// Position? position; +// if (isGetLocation) { +// if (b && +// permission != LocationPermission.denied && +// permission != LocationPermission.deniedForever) { +// try { +// position = await Geolocator.getCurrentPosition( +// locationSettings: +// const LocationSettings(timeLimit: Duration(seconds: 5))); +// print("$position"); +// } catch (e) { +// print("error $e"); +// } +// } +// } +// return position; +// } + +/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态, +/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回 +/// ------开始----- +Future openGeolocatorAppSettingsAndWait() async { + final geolocatorAppSettingsLifecycleState = + GeolocatorAppSettingsLifecycleEventHandler(); + + WidgetsBinding.instance.addObserver(geolocatorAppSettingsLifecycleState); + + // await Geolocator.openAppSettings(); + + await geolocatorAppSettingsLifecycleState.waitForResume(); + + WidgetsBinding.instance.removeObserver(geolocatorAppSettingsLifecycleState); + + print('AppSettings have been opened and user has returned'); +} + +class GeolocatorAppSettingsLifecycleEventHandler + extends WidgetsBindingObserver { + final Completer _completer = Completer(); + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed && !_completer.isCompleted) { + _completer.complete(); + } + } + + Future waitForResume() => _completer.future; +} + +/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态, +/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回 +/// ------结束----- + +/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态, +/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回 +/// ------开始----- +Future openGeolocatorLocationSettingsAndWait() async { + final geolocatorLocationSettingsLifecycleState = + GeolocatorLocationSettingsLifecycleEventHandler(); + + WidgetsBinding.instance.addObserver(geolocatorLocationSettingsLifecycleState); + + // await Geolocator.openLocationSettings(); + + await geolocatorLocationSettingsLifecycleState.waitForResume(); + + WidgetsBinding.instance + .removeObserver(geolocatorLocationSettingsLifecycleState); + + print('LocationSettings have been opened and user has returned'); +} + +class GeolocatorLocationSettingsLifecycleEventHandler + extends WidgetsBindingObserver { + final Completer _completer = Completer(); + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed && !_completer.isCompleted) { + _completer.complete(); + } + } + + Future waitForResume() => _completer.future; +} + +/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态, +/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回 +/// ------结束----- + +Future requestBluetoothPermission() async { + if (Platform.isIOS) { + PermissionStatus isBleGranted = await Permission.bluetooth.request(); + print('checkBlePermissions-ios, isBleGranted=$isBleGranted'); + if (isBleGranted.isGranted) { + startBluetoothScanning(); + return true; + } else { + showToast("蓝牙开关或蓝牙权限未开启,请开启蓝牙开关与蓝牙权限", closeTime: 7); + await showCustomConfirmAndCancelDialog( + Get.context!, "请“设置-蓝牙”中打开蓝牙开关或者在“设置-APP”中找到对应APP开启蓝牙权限", + confirmName: "去设置") + .then((msg) async { + if (msg == "confirm") { + openAppSettings(); + } + }); + return false; + } + } else if (Platform.isAndroid) { + // 检查蓝牙扫描权限 + String error = ""; + bool isShowDialog = false; + if (!await Permission.bluetoothScan.isGranted) { + if (!isShowDialog) { + isShowDialog = true; + showPermissionInfoDialog(Get.context!, permissionInfo); + } + PermissionStatus status = await Permission.bluetoothScan.request(); + if (!status.isGranted) { + error += "蓝牙扫描权限未开启,请开启附近设备权限"; + } + print("蓝牙扫描 $status"); + } + + // 检查蓝牙连接权限 + if (!await Permission.bluetoothConnect.isGranted) { + if (!isShowDialog) { + isShowDialog = true; + showPermissionInfoDialog(Get.context!, permissionInfo); + } + PermissionStatus status = await Permission.bluetoothConnect.request(); + if (!status.isGranted) { + if (error.isNotEmpty) { + error += "\n"; + } + error += "蓝牙连接权限未开启,请开启蓝牙权限"; + } + print("蓝牙连接 $status"); + } + + // 检查位置权限 + if (!await Permission.location.isGranted) { + if (!isShowDialog) { + isShowDialog = true; + showPermissionInfoDialog(Get.context!, permissionInfo); + } + //检查 + PermissionStatus status = await Permission.location.request(); + print("位置权限 $status"); + if (!status.isGranted) { + await showCustomConfirmAndCancelDialog(Get.context!, "请开启位置权限(打开精确位置)", + confirmName: "去设置") + .then((msg) async { + if (msg == "confirm") { + await openGeolocatorAppSettingsAndWait(); + // await Future.delayed(const Duration(seconds: 2)); + print('Proceeding with other operations'); + status = await Permission.location.request(); + } + }); + } + if (!status.isGranted) { + if (error.isNotEmpty) { + error += "\n"; + } + error += "位置权限未开启,请开启位置权限"; + } + } + + if (isShowDialog) { + Get.back(); + } + + if (await Permission.bluetoothScan.isGranted && + await Permission.bluetoothConnect.isGranted && + await Permission.location.isGranted) { + // bool b = await Geolocator.isLocationServiceEnabled(); + bool b =false; + if (b == false) { + await showCustomConfirmAndCancelDialog(Get.context!, "请开启系统位置开关", + confirmName: "去设置") + .then((msg) async { + if (msg == "confirm") { + // bool isOpen = await Geolocator.openLocationSettings(); + await openGeolocatorLocationSettingsAndWait(); + // await Future.delayed(const Duration(seconds: 2)); + print('Proceeding with other operations'); + // b = await Geolocator.isLocationServiceEnabled(); + } + }); + } + if (b) { + isBleStart = true; + startBluetoothScanning(); + return true; + } else { + showToast("系统位置开关未开启,请开启系统位置开关", closeTime: 7); + return false; + } + } else { + Timer(Duration.zero, () async { + showToast(error, closeTime: 7); + }); + return false; + } + } else { + showToast("当前系统不支持蓝牙,无法使用此功能", closeTime: 7); + return false; + } +} + +void find() { + int len = devices.length; + String reg = findInput.toLowerCase().replaceAll(RegExp("[::]"), ""); + List list = devices.values.toList(); + for (int i = 0; i < len; i++) { + Map d = list[i]; + bool flag = d['rssi'] >= rssichange; + if (flag) { + bool a = d['name'].toString().toLowerCase().contains(reg); + a = a || + d['id'] + .toString() + .toLowerCase() + .replaceAll(RegExp("[::]"), "") + .contains(reg); + // if(d.adData && d.adData.deviceId) { + // a = a || d.adData.deviceId.toLowerCase().replace(/[::]/g, "").indexOf(reg) > -1 + // } + // if(d.adData && d.adData.version && reg) { + // a = a || (d.adData.version + "").indexOf(reg) > -1 + // } + flag = flag && a; + } + if (flag) { + flag = flag && + DateTime.now().millisecondsSinceEpoch - d['updateTime'] < closeTime; + } + if (!flag) { + d['isClose'] = true; + } else { + d['isClose'] = false; + } + } + var result = list.where((item) => item['isClose'] == false).toList(); + if (result == null) { + findCall?.call([]); + } else { + // print(result); + result.sort((a, b) { + // print("${a['rssi']},${b['rssi']}"); + return b['rssi'] - a['rssi']; + }); + if (result.length > 0) { + findCall?.call(result.where((d) => d?['adData'] != null).toList()); + } else { + findCall?.call(result); + } + } +} + +String ab2str(List buffer) { + return buffer.map((x) => x.toRadixString(16).padLeft(2, '0')).join(''); +} + +void advertisDataFormatter(var a, item) { + Map obj = {}; + try { + if (a[2] == 1) { + obj['sn'] = a[3]; + obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase(); + obj['b'] = a[10]; + obj['h'] = a[11]; + obj['t'] = a[12]; + item['adData'] = obj; + } else if (a[2] == 2) { + obj['sn'] = a[3]; + obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase(); + obj['b'] = a[10]; + obj['h'] = a[11]; + obj['t'] = a[12]; + obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线'; + obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常'; + ByteData byteData = ByteData.sublistView( + Uint8List.fromList(a.sublist(14, 18).reversed.toList())); + obj['version'] = byteData.getUint32(0); + item['adData'] = obj; + } else if (a[2] == 3) { + List otherstr = []; + obj['sn'] = a[3]; + obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase(); + obj['b'] = a[10]; + obj['h'] = a[11]; + obj['t'] = a[12]; + obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线'; + obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常'; + + if ((a[13] & 4) == 4) { + otherstr.add('呼吸暂停'); + } + + if ((a[13] & 8) == 8 && (a[13] & 1) == 1) { + obj['isbed'] = '在床'; + } else { + obj['isbed'] = '离床'; + } + + if ((a[13] & 16) == 16) { + otherstr.add('授权过期'); + } + + if ((a[13] & 64) == 64) { + otherstr.add('设备休眠'); + } + + obj['other'] = otherstr.join('、'); + + ByteData byteData = ByteData.sublistView( + Uint8List.fromList(a.sublist(14, 18).reversed.toList())); + obj['version'] = byteData.getUint32(0); + + ByteData qsnData = + ByteData.sublistView(Uint8List.fromList(a.sublist(17, 19))); + obj['qsn'] = qsnData.getUint16(0) * 256 + obj['sn']; + + item['adData'] = obj; + } else if (a.length > 17) { + obj['sn'] = a[3]; + obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase(); + obj['b'] = a[10]; + obj['h'] = a[11]; + obj['t'] = a[12]; + obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线'; + obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常'; + + ByteData byteData = ByteData.sublistView( + Uint8List.fromList(a.sublist(14, 18).reversed.toList())); + obj['version'] = byteData.getUint32(0); + + item['adData'] = obj; + } + } catch (e) { + print(e); + } +} + +void startBluetoothScanning() async { + // 开始扫描附近的蓝牙设备 + if (FlutterBluePlus.isScanningNow) { + await FlutterBluePlus.stopScan(); + } + FlutterBluePlus.startScan(timeout: const Duration(seconds: 15)); +} + +getOneConnectedDeviceProp(id) { + return connectList[id]; +} + +void setOther(device, connectedDeviceProp, fun) async { + try { + List services = await device.discoverServices(); + print(services); + bool isNotify = false; + bool isWrite = false; + for (var service in services) { + if (connectedDeviceProp.connectedDevicePropType == + ConnectedDevicePropType.JunHe) { + if (service.uuid.str128.toUpperCase() != myuuid) { + continue; + } + } + // print("serviece $service"); + for (BluetoothCharacteristic element in service.characteristics) { + if (isNotify == false && element.properties.notify) { + await element.setNotifyValue(true); + print("setNotifyValue 完成"); + connectedDeviceProp.createLisetenReceive(element); + isNotify = true; + if (connectedDeviceProp.connectedDevicePropType == + ConnectedDevicePropType.JunHe) { + continue; + } + if (connectedDeviceProp.connectedDevicePropType == + ConnectedDevicePropType.MHT) { + continue; + } + } + if (isWrite == false && element.properties.write) { + connectedDeviceProp?.writeCharacteristic = element; + isWrite = true; + print("$element"); + } + } + } + if (!isWrite || !isNotify) { + if (connectedDeviceProp != null) { + disconnect(connectedDeviceProp!); + } + print("service 订阅失败 isWrite $isWrite isNotify $isNotify"); + fun['fail']?.call("service 订阅失败 isWrite $isWrite isNotify $isNotify"); + return; + } + print("service 注册完成"); + connectList[connectedDeviceProp.id] = connectedDeviceProp; + connectedDeviceProp.createListenState(); + if (connectedDeviceProp.connectedDevicePropType == + ConnectedDevicePropType.JunHe) { + connectedDeviceProp.heartbeat(); + } + print("回调成功"); + fun['success']?.call(connectedDeviceProp); + } catch (e) { + print("连接失败 执行失败回调 错误: $e"); + if (connectedDeviceProp != null) { + disconnect(connectedDeviceProp!); + } + print("连接失败 执行失败回调"); + fun['fail']?.call(e); + } +} + +// 连接设备 +void connectToDevice(fun) async { + BluetoothDevice device = fun['device']; + ConnectedDeviceProp? connectedDeviceProp = + getOneConnectedDeviceProp(device.remoteId.str); + if (connectedDeviceProp != null) { + disconnect(connectedDeviceProp); + Future.delayed(const Duration(seconds: 1), () { + connectToDevice(fun); + }); + return; + } + try { + print("connecting"); + await device.connect(timeout: const Duration(seconds: 8)); + print("device.connect success"); + ConnectedDevicePropType connectedDevicePropType = + ConnectedDevicePropType.JunHe; + if (isQuanShiDevice(device.advName)) { + connectedDevicePropType = ConnectedDevicePropType.QuanShi; + } else if (isMHTSWES(device.advName)) { + connectedDevicePropType = ConnectedDevicePropType.MHT; + } + connectedDeviceProp = ConnectedDeviceProp( + connectDevice: device, + fun: fun, + connectedDevicePropType: connectedDevicePropType); + if (Platform.isAndroid) { + await device.requestMtu(mcuMax); + } + Timer(const Duration(milliseconds: 1000), () { + setOther(device, connectedDeviceProp, fun); + }); + } catch (e) { + print("连接失败 执行失败回调 错误: $e"); + if (connectedDeviceProp != null) { + disconnect(connectedDeviceProp!); + } + print("连接失败 执行失败回调"); + fun['fail']?.call(e); + } +} + +void disconnect(ConnectedDeviceProp connectedDeviceProp) { + connectedDeviceProp.closeHeartBeat(); + connectList.remove(connectedDeviceProp.id); + connectedDeviceProp.closeConnectedDeviceProp(); +} + +void closeAll() { + findCall = null; + connectList.values.toList().forEach((element) { + disconnect(element); + }); +} + +enum ConnectedDevicePropType { JunHe, QuanShi, MHT } + +class ConnectedDeviceProp { + ConnectedDevicePropType connectedDevicePropType; + Timer? heartbeatTimer = null; + int _seq = 0; + var connectDevice; + var writeCharacteristic; + var listenState; + StreamSubscription>? lisetenReceive; + Map fun; + List receiveMethods = []; + List logList = []; + Function? logChange; + ConnectedDeviceProp( + {required this.connectDevice, + required Map this.fun, + this.connectedDevicePropType = ConnectedDevicePropType.JunHe}); + List receiveLogArr = []; + int deviceType = 2; + int encodeType = 2; + List sendArr = []; + double sendExecAverage = 100; + bool isClose = false; + + String get id { + return connectDevice.remoteId.str; + } + + int sum_ab(dv) { + ByteData sum = ByteData(1); + for (int i = 0; i < dv.buffer.lengthInBytes; i++) { + sum.setUint8(0, dv.getUint8(i) + sum.getUint8(0)); + } + return sum.getUint8(0); + } + + void heartbeat() { + closeHeartBeat(); + heartbeatTimer = Timer.periodic(const Duration(seconds: 8), (timer) { + ByteData dv = ByteData(4); + dv.setUint8(0, 4); + dv.setUint8(2, seq); + dv.setUint8(3, 5); + dv.setUint8(1, sum_ab(dv)); + writeBle(dv); + }); + } + + closeHeartBeat() { + if (heartbeatTimer != null) { + heartbeatTimer!.cancel(); + heartbeatTimer = null; + } + } + + ByteData str2ab_oneByte(String str, {int startLength = 0}) { + Uint8List utf8str = utf8.encode(str); + int len = utf8str.length + startLength; + ByteData buf2 = ByteData.sublistView(utf8str); + ByteData buf = ByteData(len); + for (int i = startLength; i < len; i++) { + buf.setUint8(i, buf2.getUint8(i - startLength)); + } + return buf; + } + + void write3OfString(sendDate, {Function? success, Function? fail}) { + ByteData dv = str2ab_oneByte(sendDate, startLength: 4); + int len = dv.buffer.lengthInBytes; + dv.setUint8(0, len); + dv.setUint8(2, seq); + dv.setUint8(3, 8 * 16 + 3); + dv.setUint8(1, sum_ab(dv)); + writeBle(dv, success: success, fail: fail); + } + + void writeBle(ByteData d, {Function? success, Function? fail}) { + Uint8List d_ = Uint8List.view(d.buffer); + if (sendArr.length == 0) { + write(d_, success, fail); + } + sendArr.insert(0, {"d": d_, "success": success, "fail": fail}); + } + + void write(Uint8List d, Function? success, Function? fail, {int exec = 100}) { + if (writeCharacteristic != null) { + // try { + // if (d[3] == 8 * 16 + 3) { + // print( + // "blewrite s = $sendExecAverage d = ${utf8.decode(d.sublist(4))}"); + // } else { + // print("ble last write d = ${d[3]}"); + // } + // } catch (e) { + // print("write logprint error $e"); + // } + writeCharacteristic.write(d, withoutResponse: true).then((e) { + // print("write success $e"); + if (connectedDevicePropType != ConnectedDevicePropType.JunHe) { + print("发送 $d"); + } + if (exec > 95) { + sendExecAverage = sendExecAverage + 0.5; + } + if (sendExecAverage > 99) { + sendExecAverage = 99; + } + if (sendArr.length > 0) { + sendArr.removeLast(); + Map last = sendArr.last; + write(last["d"], last["success"], last["fail"]); + } + success?.call(); + }).catchError((e) { + // print("exec = $exec , $e"); + if (exec < 0) { + print("$e"); + fail?.call(); + } + if (exec > -1 && isClose == false) { + int time = ((100.0 - sendExecAverage) * 5.0).toInt(); + if (exec < 80) { + time = (100 - exec) * 5; + sendExecAverage = exec * 1.0; + } else { + sendExecAverage = sendExecAverage - (100 - exec) * 0.1; + } + Timer(Duration(milliseconds: time), () { + write(d, success, fail, exec: exec - 1); + }); + } + }); + } + } + + void read6() { + ByteData dv = ByteData(4); + dv.setUint8(0, 4); + dv.setUint8(2, seq); + dv.setUint8(3, 6); + dv.setUint8(1, sum_ab(dv)); + writeBle(dv); + } + + addLog(String log) { + if (logList.length > 500) { + logList.removeRange(0, 50); + } + DateTime date = DateTime.now(); + String h = date.hour > 10 ? "${date.hour}" : "0${date.hour}"; + String m = date.minute > 10 ? "${date.minute}" : "0${date.minute}"; + String s = date.second > 10 ? "${date.second}" : "0${date.second}"; + logList.add({"time": "$h:$m:$s", "value": log}); + print("ble $id log: $log"); + if (logChange != null) { + logChange?.call(logList, log); + } + } + + createListenState() { + listenState = connectDevice.connectionState.listen((state) { + print('ble Device state $id $state'); + if (state == BluetoothConnectionState.disconnected) { + print('ble Device state $id disconnected'); + isClose = true; + disconnect(this); + fun['stateChange']?.call(state, this); + } + }); + } + + createLisetenReceive(BluetoothCharacteristic element) { + lisetenReceive = element.onValueReceived.listen((List value) { + if (connectedDevicePropType == ConnectedDevicePropType.JunHe) { + if (value.isEmpty) { + return; + } + bool isOk = sumCheck(value); + if (isOk) { + receiveMethods.forEach((m) { + m?.call(); + }); + yewuSwitch(value[3], value.sublist(4)); + } + } else { + print("onValueReceived $value"); + receiveLogArr.forEach((m) { + m(value); + }); + } + }); + } + + closeConnectedDeviceProp() { + isClose = true; + if (listenState != null) { + listenState?.cancel(); + } + if (lisetenReceive != null) { + lisetenReceive?.cancel(); + } + connectDevice?.disconnect(); + } + + int get seq { + int r = _seq % 256; + _seq++; + return r; + } + + bool sumCheck(List ab) { + ByteData dv = ByteData.sublistView(Uint8List.fromList(ab)); + + if (dv.getUint8(0) != ab.length) { + print("和校验失败:长度不对"); + return false; + } //和校验失败 + + if (sumList(ab) != dv.getUint8(1)) { + print("和校验失败: 校验失败"); + return false; + } + + return true; + } + + int sumList(List ab) { + ByteData dv = ByteData.sublistView(Uint8List.fromList(ab)); + ByteData sum = ByteData.sublistView(Uint8List(1)); + + sum.setUint8(0, dv.getUint8(0)); + + for (int i = 2; i < ab.length; i++) { + sum.setUint8(0, dv.getUint8(i) + sum.getUint8(0)); + } + + return sum.getUint8(0); + } + + List endLogValue = []; + Timer? endLogTimer; + + void yewuSwitch(int yewu, List abData) { + switch (yewu) { + case 7: + String error = ab2StrByType(abData); + print(error); + break; + + case 131: + List? logData; + if (abData.last != 10) { + int index = abData.lastIndexOf(10); + if (index == -1) { + index = abData.length; + endLogValue = [...endLogValue, ...abData.sublist(0, index)]; + } else { + logData = [...endLogValue, ...abData.sublist(0, index)]; + endLogValue = abData.sublist(index); + } + // if (index == -1) { + // index = abData.length; + // } + // if(endLogValue.isNotEmpty) { + // logData = [...endLogValue, ...abData.sublist(0, index)]; + // endLogValue = []; + // } else { + // logData = [...abData.sublist(0, index)]; + // } + // if(index != abData.length) { + // endLogValue = abData.sublist(index); + // } + } else { + int index = abData.length; + + if (endLogValue.isNotEmpty) { + logData = [...endLogValue, ...abData]; + } else { + logData = abData; + } + + endLogValue = []; + } + + if (endLogTimer != null) { + endLogTimer!.cancel(); + endLogTimer = null; + } + + if (endLogValue != null && endLogValue!.isNotEmpty) { + endLogTimer = Timer(Duration(milliseconds: 400), () { + String log = ab2StrByType(endLogValue!); + endLogValue = []; + addLog(log); + try { + receiveLogArr.forEach((m) { + m(log); + }); + } catch (e) { + print(e); + } + }); + } + + if (logData != null && logData.isNotEmpty) { + if (logData.length != 1 || logData[0] != 13) { + String log = ab2StrByType(logData!); + addLog(log); + try { + receiveLogArr.forEach((m) { + m(log); + }); + } catch (e) { + print(e); + } + } + } + // 处理逻辑 + break; + + case 132: + ByteData dv = ByteData.sublistView(Uint8List.fromList(abData)); + + for (int i = 0; i < abData.length;) { + int len = dv.getUint8(i); + yewuSwitch(dv.getUint8(i + 1), abData.sublist(i + 2, i + 1 + len)); + i = i + 1 + len; + } + + break; + + default: + break; + } + } + + String ab2StrByType(List abData) { + // Implement your logic for converting abData to String + String str = ""; + if (abData.isNotEmpty) { + try { + str = utf8.decode(abData); + } catch (e) { + str = "解析错误"; + } + } + return str; + } +} diff --git a/lib/component/home_page/SleepDataModuleWidget.dart b/lib/component/home_page/SleepDataModuleWidget.dart new file mode 100644 index 0000000..abaee63 --- /dev/null +++ b/lib/component/home_page/SleepDataModuleWidget.dart @@ -0,0 +1,145 @@ +import 'package:ef/base/widget/flutterflow/FlutterFlowTheme.dart'; +import 'package:ef/ef.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/controller/theme_controller/ThemeController.dart'; + +class SleepDataModuleWidget extends StatefulWidget { + const SleepDataModuleWidget({super.key}); + + @override + State createState() => _SleepDataModuleWidgetState(); +} + +class _SleepDataModuleWidgetState extends State { + @override + void setState(VoidCallback callback) { + super.setState(callback); + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + ThemeController themeController = Get.find(); + return Container( + width: MediaQuery.sizeOf(context).width * 0.27, + constraints: BoxConstraints( + minWidth: 200.rpx, + ), + decoration: BoxDecoration( + color: stringToColor("#313541"), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(18.rpx, 18.rpx, 18.rpx, 22.rpx), + child: Container( + decoration: BoxDecoration(), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '离床次数', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + // SizedBox( + // height: 21.rpx, + // ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + '4', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 40.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0, 0.rpx, 10.rpx), + child: Text( + '次', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants().small_text_fontSize, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ], + ), + Container( + width: MediaQuery.sizeOf(context).width * 0.07, + height: MediaQuery.sizeOf(context).height * 0.014, + constraints: BoxConstraints( + minWidth: 43.rpx, + minHeight: 36.rpx, + ), + decoration: BoxDecoration(), + child: FFButtonWidget( + onPressed: () { + print('Button pressed ...'); + }, + text: '异常', + options: FFButtonOptions( + height: 40.rpx, + padding: + EdgeInsetsDirectional.fromSTEB(0.rpx, 0, 0.rpx, 0), + color: stringToColor("#FF7159"), + textStyle: + FlutterFlowTheme.of(context).titleSmall.override( + fontFamily: 'Inter Tight', + color: themeController.currentColor.sc3, + letterSpacing: 0.0, + fontSize: 15.rpx, + ), + elevation: 0, + borderRadius: BorderRadius.circular(8.rpx), + ), + ), + ), + ], + ), + Text( + '正常值:0~2', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: AppConstants().small_text_fontSize, + letterSpacing: 0.0, + color: Colors.grey), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/component/home_page/SleepDateWidget.dart b/lib/component/home_page/SleepDateWidget.dart new file mode 100644 index 0000000..38e2e14 --- /dev/null +++ b/lib/component/home_page/SleepDateWidget.dart @@ -0,0 +1,136 @@ +import 'package:ef/base/widget/flutterflow/FlutterFlowTheme.dart'; +import 'package:ef/ef.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/tool/ClickableContainer.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +class SleepDateWidget extends StatefulWidget { + const SleepDateWidget({super.key}); + + @override + State createState() => _SleepDateWidgetState(); +} + +class _SleepDateWidgetState extends State { + @override + Widget build(BuildContext context) { + ThemeController themeController = Get.find(); + return ClickableContainer( + backgroundColor: Colors.transparent, // 原 BoxDecoration 为空 + highlightColor: + themeController.currentColor.sc3.withOpacity(0.1), // 自定义点击波纹颜色 + borderRadius: AppConstants().normal_container_radius, // 原来没设置圆角 + padding: EdgeInsets.zero, + onTap: () { + print("今日评分卡片点击"); + }, + child: Container( + width: MediaQuery.sizeOf(context).width * 0.19, + constraints: BoxConstraints( + minWidth: 143.rpx, + ), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(10.rpx, 25.rpx, 10.rpx, 22.rpx), + child: Container( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 14.rpx), + child: Text( + '今日', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: AppConstants().title_text_fontSize, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 33.rpx), + child: Text( + '07/15', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 20.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 16.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '70', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 48.rpx, + letterSpacing: 0.0, + color: stringToColor("#00C1AA")), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 16.rpx, 0, 0.rpx), + child: Text( + '分', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants().small_text_fontSize, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ], + ), + ), + Container( + width: 0.2.rpx, + height: 2.4.rpx, + constraints: BoxConstraints( + minWidth: 123.rpx, + minHeight: 47.rpx, + ), + child: FFButtonWidget( + onPressed: () { + print('合格按钮点击'); + }, + text: '合格', + options: FFButtonOptions( + height: 40.rpx, + padding: + EdgeInsetsDirectional.fromSTEB(16.rpx, 0, 16.rpx, 0), + iconPadding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: stringToColor("#00C1AA"), + textStyle: + FlutterFlowTheme.of(context).titleSmall.override( + fontFamily: 'Inter Tight', + color: Colors.white, + letterSpacing: 0.0, + ), + elevation: 0, + borderRadius: BorderRadius.circular(50.rpx), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/component/tool/ClickableContainer.dart b/lib/component/tool/ClickableContainer.dart new file mode 100644 index 0000000..ac5f1f4 --- /dev/null +++ b/lib/component/tool/ClickableContainer.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +class ClickableContainer extends StatelessWidget { + final Color backgroundColor; // 容器背景色 + final Color highlightColor; // 点击时背景色 + final EdgeInsetsGeometry padding; // 内部间距 + final VoidCallback onTap; // 点击回调 + final Widget child; // 子组件 + final double borderRadius; // 容器圆角(可选,默认为0) + + const ClickableContainer({ + Key? key, + required this.backgroundColor, + required this.highlightColor, + required this.padding, + required this.onTap, + required this.child, + this.borderRadius = 0, // 默认无圆角 + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Material( + color: Colors.transparent, + child: Ink( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular(borderRadius), + ), + child: InkWell( + borderRadius: BorderRadius.circular(borderRadius), + onTap: onTap, + splashColor: highlightColor.withOpacity(0.3), // 点击时的波纹效果 + child: Padding( + padding: padding, + child: child, // 内容自适应 + ), + ), + ), + ), + ); + } +} diff --git a/lib/component/tool/CustomCard.dart b/lib/component/tool/CustomCard.dart index a5b4118..1c6975d 100644 --- a/lib/component/tool/CustomCard.dart +++ b/lib/component/tool/CustomCard.dart @@ -5,7 +5,6 @@ class CustomCard extends StatefulWidget { final VoidCallback onTap; // 点击回调 final List colors; // 背景颜色列表 final Widget child; // 子组件 - final String title; // 标题 final bool enableAnimation; // 是否启用动画效果 final bool enableGradient; // 是否启用渐变 @@ -15,7 +14,6 @@ class CustomCard extends StatefulWidget { required this.onTap, required this.colors, required this.child, - required this.title, this.enableAnimation = true, // 默认启用动画效果 this.enableGradient = true, // 默认启用渐变效果 }) : super(key: key); diff --git a/lib/component/tool/FrostedDialog.dart b/lib/component/tool/FrostedDialog.dart new file mode 100644 index 0000000..d77971b --- /dev/null +++ b/lib/component/tool/FrostedDialog.dart @@ -0,0 +1,37 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; + +class FrostedDialog extends StatelessWidget { + final Widget child; + final double blurSigma; + final Color barrierColor; + + const FrostedDialog({ + super.key, + required this.child, + this.blurSigma = 5.0, + this.barrierColor = const Color.fromRGBO(0, 0, 0, 0.5), + }); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + // 毛玻璃背景 + BackdropFilter( + filter: ImageFilter.blur(sigmaX: blurSigma, sigmaY: blurSigma), + child: Container( + color: Colors.transparent, + ), + ), + Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20.0), + ), + backgroundColor: Colors.transparent, // 背景透明,由 child 自己决定 + child: child, + ), + ], + ); + } +} diff --git a/lib/controller/device/blueteeth_bind_controller.dart b/lib/controller/device/blueteeth_bind_controller.dart index adf8380..573fdce 100644 --- a/lib/controller/device/blueteeth_bind_controller.dart +++ b/lib/controller/device/blueteeth_bind_controller.dart @@ -5,7 +5,24 @@ part 'blueteeth_bind_controller.g.dart'; // 由json_serializable自动生成的 @JsonSerializable() class BlueteethBindModel { - int read = 1;//是否不再提示教程 0 不再提示 1 需要提示 + int? read = 1; //是否不再提示教程 0 不再提示 1 需要提示 + double? singal = -70; //扫描信号强度 + + List? devicelist = []; //蓝牙扫描到的设备数据列表 + List? blelist = []; //蓝牙扫描到的设备数据列表 + List? wifiList = []; + + List bindArr = ["", "", ""]; + + String connectedWifiName = ""; + + int connectedRssi = 0; + + String deviceName = ""; + + bool? deviceIndex0 = true; + bool? deviceIndex1 = false; + bool? deviceIndex2 = false; BlueteethBindModel(); @@ -27,5 +44,23 @@ class BlueteethBindModel { class BlueteethBindController extends GetControllerEx { BlueteethBindController() { attr = GetModel(BlueteethBindModel()).obs; - } -} \ No newline at end of file + } + + void updateDeviceStatus() { + // try { + + // } catch (e) { + // print(e); + // EasyDartModule.logger.info("向后端请求设备绑定状态报错了:$e"); + // } finally { + // EasyDartModule.logger.info("向后端请求设备绑定状态"); + // } + } + + Future bindDevice(d) async { + print("绑定参数:$d"); + await Future.delayed(Duration(seconds: 1)); + // return ApiService.request + // .post("/api/device/info/bind", data: formdata.FormData.fromMap(d)); + } +} diff --git a/lib/controller/login/login_controller.dart b/lib/controller/login/login_controller.dart new file mode 100644 index 0000000..410168d --- /dev/null +++ b/lib/controller/login/login_controller.dart @@ -0,0 +1,175 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; + +import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +part 'login_controller.g.dart'; + +@JsonSerializable() +class LoginModel { + //版本id + int? loginStyle = 1; //1.密码登录 2.短信登录 + String? account = '17649984946'; //账户 + String? password = 'wyf123,.'; //密码 + + String? phone; //手机号 + String? code; //验证码 + + String? register_code; + + bool? showPd = true; + + int? forceLogin = 0; + + bool? isIos; //是否为ios设备 + + bool? isWeChatNotInstalled; //是否安装微信 + + bool? register_agree = false; //是否同意协议 + + LoginModel(); + + static LoginModel fromJson(Map json) => + _$LoginModelFromJson(json); + Map toJson() => _$LoginModelToJson(this); +} + +class LoginController extends GetControllerEx { + // 初始化实例 + // final Fluwx fluwx = Fluwx(); + // 微信监听返回值 + // FluwxCancelable? fluwxCancelable; + + // final UserRepository repository = UserRepository(); + LoginController() { + attr = GetModel(LoginModel()).obs; + } + + //登录 + Future login(BuildContext context) async { + // return ''; + String message = ''; + String account = ''; + String password = ''; + // if (model.loginStyle == 1) { + // //账号登录 + // if (model.account == null || model.account!.isEmpty) { + // message = '账户不能为空'; + // showToast(message); + // return message; + // } + // if (model.password == null || model.password!.isEmpty) { + // message = '密码不能为空'; + // showToast(message); + // return message; + // } + // account = model.account!; + // password = model.password!; + // } + // if (model.loginStyle == 2) { + // //账号登录 + // if (model.phone == null || model.phone!.isEmpty) { + // message = '手机号不能为空'; + + // showToast(message); + // return message; + // } + // if (!MyUtils.isValidPhoneNumber(model.phone!)) { + // message = '请输入正确的手机号'; + + // showToast(message); + // return message; + // } + // if (model.code == null || model.code!.isEmpty) { + // message = '验证码不能为空'; + + // showToast(message); + // return message; + // } + // account = model.phone!; + // password = model.code!; + // } + // RegisterController registerController = Get.find(); + // if (registerController.model.register_agree == null || + // registerController.model.register_agree != true) { + // message = "需要同意协议"; + // showToast(message); + // return message; + // } + // message = await repository.login( + // model.loginStyle!, account, password, model.forceLogin); + // model.forceLogin = 0; + + return message; + } + + Future getCode(BuildContext context) async { + String message = ""; + if (model.register_agree == null || model.register_agree != true) { + // message = "需要同意协议"; + // showToast(message); + return message; + } + if (model.phone == null || model.phone!.isEmpty) { + message = "请输入手机号"; + // showToast(message); + return message; + } + if (!MyUtils.isValidPhoneNumber(model.phone!)) { + message = '请输入正确的手机号'; + showToast(message); + return message; + } + // message = await repository.sendRegisterCode(model.phone!); + if (message.isNotEmpty) { + showToast(message ?? "发送失败,请稍后再试!"); + return "发送失败,请稍后再试!"; + } else { + showToast("发送验证码成功!", color: color_success); + } + return ''; + } + + //微信登录 + // Future wxLoginSendAuth() async { + // /* + // 1、目前移动应用上微信登录只提供原生的登录方式,需要用户安装微信客户端才能配合使用。 + // 2、对于Android应用,建议总是显示微信登录按钮,当用户手机没有安装微信客户端时,请引导用户下载安装微信客户端。 + // 3、对于iOS应用,考虑到iOS应用商店审核指南中的相关规定,建议开发者接入微信登录时,先检测用户手机是否已安装微信客户端 + // (使用sdk中isWXAppInstalled函数 ),对未安装的用户隐藏微信登录按钮,只提供其他登录方式(比如手机号注册登录、游客登录等) + // */ + // if (isAndroid) { + // bool isWeChatInstalled = await fluwx.isWeChatInstalled; + // debugPrint('is wechat installed: $isWeChatInstalled'); + // if (!isWeChatInstalled) { + // showToast("请先安装微信APP,再使用微信登录"); + // return; + // } + // } + // fluwx + // .authBy( + // which: NormalAuth( + // scope: 'snsapi_userinfo', + // state: 'wechat_sdk_zhmht_wxlogin', + // )) + // .then((data) { + // //返回true表示成功或者false表示失败,这边没有意义从login_controller页面构造函数监听中去处理 + // debugPrint('msg:$data'); + // }); + // } + + //退出登录 + // Future logout() async { + // await repository.logout(); + // } + + // loginByWechatCode(String code) async { + // return await repository.loginByWechatCode(code); + // } + + //注销账号 + // deletedAccount() async { + // return await repository.deletedAccount(); + // } +} diff --git a/lib/controller/login/login_controller.g.dart b/lib/controller/login/login_controller.g.dart new file mode 100644 index 0000000..05c1dc7 --- /dev/null +++ b/lib/controller/login/login_controller.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'login_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LoginModel _$LoginModelFromJson(Map json) => LoginModel() + ..loginStyle = (json['loginStyle'] as num?)?.toInt() + ..account = json['account'] as String? + ..password = json['password'] as String? + ..phone = json['phone'] as String? + ..code = json['code'] as String? + ..register_code = json['register_code'] as String? + ..showPd = json['showPd'] as bool? + ..forceLogin = (json['forceLogin'] as num?)?.toInt() + ..isIos = json['isIos'] as bool? + ..isWeChatNotInstalled = json['isWeChatNotInstalled'] as bool?; + +Map _$LoginModelToJson(LoginModel instance) => + { + 'loginStyle': instance.loginStyle, + 'account': instance.account, + 'password': instance.password, + 'phone': instance.phone, + 'code': instance.code, + 'register_code': instance.register_code, + 'showPd': instance.showPd, + 'forceLogin': instance.forceLogin, + 'isIos': instance.isIos, + 'isWeChatNotInstalled': instance.isWeChatNotInstalled, + }; diff --git a/lib/controller/main_bottom/global_controller.dart b/lib/controller/main_bottom/global_controller.dart index 8772ee5..6373562 100644 --- a/lib/controller/main_bottom/global_controller.dart +++ b/lib/controller/main_bottom/global_controller.dart @@ -1,8 +1,10 @@ import 'dart:async'; +import 'package:EasyDartModule/EasyDartModule.dart'; import 'package:ef/ef.dart'; import 'package:vbvs_app/controller/main_bottom/main_page_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/pages/common/selectDialog.dart'; class GlobalModel { List deviceList = []; @@ -138,16 +140,11 @@ class GlobalController extends GetControllerEx { deviceUpdateTimerCreated() { if (getDeviceListTimer == null) { getDeviceListTimer = Timer.periodic(const Duration(seconds: 10), (t) { - if (userInfoController.model.token != null) { - - } + if (userInfoController.model.token != null) {} }); } } - - - getDeviceGroupName(device) { return "${device['roomName']}/${device["deviceType"]?["name"]}/${device["name"]}"; } @@ -169,4 +166,49 @@ class GlobalController extends GetControllerEx { model.deviceType = rs.where((d) => d["page"] != null).toList(); updateAll(); } + + getDeviceList({int time = 1}) async { + await EasyDartModule.dio.get("/api/device/info/list").then((d) { + Map d_ = {}; + d.data?["data"]?.forEach((item) { + if (d_[item["roomName"]] == null) { + d_[item["roomName"]] = []; + } + d_[item["roomName"]].add(item); + }); + List res_ = []; + d_.keys.forEach((key) { + res_.addAll(d_[key]); + }); + model.deviceList = res_; + if (model.deviceMain != null && model.deviceMain["mac"] != null) { + bool isClose = false; + model.deviceList.forEach((item) { + if (item["mac"] == model.deviceMain["mac"]) { + model.deviceMain = item; + isClose = true; + updateAll(); + } + }); + if (!isClose) { + model.deviceMain = {}; + updateAll(); + showCustomConfirmAndCancelDialog(Get.context!, "设备已经被解绑,是否回到主界面?") + .then((e) { + if (e == "confirm") { + Get.find().model.currentIndex = 0; + Get.offAllNamed("/mianPageBottomChange"); + } + }); + } + } else { + updateAll(); + } + }).catchError((e) { + print("$e"); + if (time > 0) { + getDeviceList(time: time - 1); + } + }); + } } diff --git a/lib/controller/person/person_controller.dart b/lib/controller/person/person_controller.dart new file mode 100644 index 0000000..e7c93e7 --- /dev/null +++ b/lib/controller/person/person_controller.dart @@ -0,0 +1,33 @@ +import 'package:ef/ef.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'person_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class PersonModel { + int read = 1; + + DateTime? birthday;//是否不再提示教程 0 不再提示 1 需要提示 + + PersonModel(); + + // 从JSON反序列化时的异常处理 + + factory PersonModel.fromJson(Map json) { + try { + return _$PersonModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return PersonModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$PersonModelToJson(this); +} + +class PersonController extends GetControllerEx { + PersonController() { + attr = GetModel(PersonModel()).obs; + } +} \ No newline at end of file diff --git a/lib/controller/person/person_controller.g.dart b/lib/controller/person/person_controller.g.dart new file mode 100644 index 0000000..004da7e --- /dev/null +++ b/lib/controller/person/person_controller.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'person_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PersonModel _$PersonModelFromJson(Map json) => PersonModel() + ..read = (json['read'] as num).toInt() + ..birthday = json['birthday'] == null + ? null + : DateTime.parse(json['birthday'] as String); + +Map _$PersonModelToJson(PersonModel instance) => + { + 'read': instance.read, + 'birthday': instance.birthday?.toIso8601String(), + }; diff --git a/lib/controller/time/countdown_controller.dart b/lib/controller/time/countdown_controller.dart new file mode 100644 index 0000000..7840138 --- /dev/null +++ b/lib/controller/time/countdown_controller.dart @@ -0,0 +1,33 @@ +import 'dart:async'; +import 'package:get/get.dart'; + +class CountdownController extends GetxController { + var countdown = 0.obs; + Timer? timer; + + @override + void onInit() { + super.onInit(); + } + + void startCountdown(int seconds) { + timer?.cancel(); // 取消之前的定时器 + countdown.value = seconds; + timer = Timer.periodic(Duration(seconds: 1), (timer) { + int elapsed = timer.tick; + int remaining = seconds - elapsed; + if (remaining > 0) { + countdown.value = remaining; + } else { + countdown.value = 0; + timer.cancel(); + } + }); + } + + @override + void onClose() { + timer?.cancel(); + super.onClose(); + } +} diff --git a/lib/controller/user_info_controller.dart b/lib/controller/user_info_controller.dart index 38d05f3..6c4e7e5 100644 --- a/lib/controller/user_info_controller.dart +++ b/lib/controller/user_info_controller.dart @@ -1,19 +1,7 @@ -import 'dart:io'; - import 'package:ef/ef.dart'; - -import 'package:get_storage/get_storage.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:path/path.dart' as p; -import 'package:uuid/uuid.dart'; -import 'package:vbvs_app/common/color/app_uri_status.dart'; -import 'package:vbvs_app/common/util/CommonVariables.dart'; - -import 'package:vbvs_app/common/util/MyUtils.dart'; -import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/user_data.dart'; - part 'user_info_controller.g.dart'; @JsonSerializable() @@ -34,9 +22,13 @@ class UserInfoModel { User? superbase_user; String? img_bucket = 'user'; - int? login = 0; + int? login = 1; //0未登录 1 登录 - bool? register_agree = false; //注册协议 + + + int? deviceBindNum = 0; //绑定设备数量 + + int? loginPhone = 0;//0 本机号码 1其他手机号 UserInfoModel(); static UserInfoModel fromJson(Map json) => diff --git a/lib/controller/user_info_controller.g.dart b/lib/controller/user_info_controller.g.dart index f8dc0b9..2243d6a 100644 --- a/lib/controller/user_info_controller.g.dart +++ b/lib/controller/user_info_controller.g.dart @@ -19,8 +19,7 @@ UserInfoModel _$UserInfoModelFromJson(Map json) => ..deviceModel = json['deviceModel'] as String? ..appVersion = json['appVersion'] as String? ..img_bucket = json['img_bucket'] as String? - ..login = (json['login'] as num?)?.toInt() - ..register_agree = json['register_agree'] as bool?; + ..login = (json['login'] as num?)?.toInt(); Map _$UserInfoModelToJson(UserInfoModel instance) => { @@ -34,5 +33,4 @@ Map _$UserInfoModelToJson(UserInfoModel instance) => 'appVersion': instance.appVersion, 'img_bucket': instance.img_bucket, 'login': instance.login, - 'register_agree': instance.register_agree, }; diff --git a/lib/main.dart b/lib/main.dart index f3e0f22..5e83c29 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,9 +4,12 @@ import 'package:flutter/services.dart'; import 'package:get_storage/get_storage.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/login/login_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/main_bottom/main_page_controller.dart'; +import 'package:vbvs_app/controller/person/person_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/time/countdown_controller.dart'; import 'package:vbvs_app/language/AppLanguage.dart'; import 'package:vbvs_app/model/CustomThemeColor.dart'; import 'package:vbvs_app/model/user_data.dart'; @@ -25,6 +28,7 @@ Future main() async { // await GetStorage.init(); // 初始化登录 await initLogin(); + await initLog(); // 检查网络 // Checknetwork.checkNetwork(); // 微信开放平台注册 @@ -41,6 +45,10 @@ Future main() async { // runApp(const MyApp()); } +initLog() { + +} + Future initLogin() async { // 初始化控制器 Get.put(UserInfoController()); @@ -155,6 +163,9 @@ class MyApp extends StatelessWidget { Get.put(GlobalController()), Get.lazyPut(() => MainPageController()), Get.lazyPut(() => BlueteethBindController()), + Get.lazyPut(() => PersonController()), + Get.lazyPut(() => CountdownController()), + Get.lazyPut(() => LoginController()), ])); }); } diff --git a/lib/model/BleDeviceData.dart b/lib/model/BleDeviceData.dart new file mode 100644 index 0000000..b4ad163 --- /dev/null +++ b/lib/model/BleDeviceData.dart @@ -0,0 +1,35 @@ +class BleDeviceData { + final int type; // 协议版本 + final int sn; // 广播包序号低8位 + final String deviceId; // 设备唯一地址(6字节) + final int bre; // 呼吸 + final int ht; // 心率 + final int active; // 体动等级 + final int flag; // 设备属性 + final int version; // 软件版本 + final int qsn; // 广播帧序列号高16位 + int? status; // 设备状态 + String? name;//设备名称 + int? rssi; + String? mac;//mac地址 + + BleDeviceData({ + required this.type, + required this.sn, + required this.deviceId, + required this.bre, + required this.ht, + required this.active, + required this.flag, + required this.version, + required this.qsn, + }); + + int get fullSeq => qsn * 256 + sn; + + bool get isOnline => (flag & 0x01) != 0; + bool get sensorError => (flag & 0x02) != 0; + bool get apnea => (flag & 0x04) != 0; + bool get inBed => (flag & 0x08) != 0; + bool get expired => (flag & 0x10) != 0; +} diff --git a/lib/pages/common/selectDialog.dart b/lib/pages/common/selectDialog.dart new file mode 100644 index 0000000..c5e0411 --- /dev/null +++ b/lib/pages/common/selectDialog.dart @@ -0,0 +1,773 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; + + +getOnePicker(context, List arr, int checkIndex, Function onSelectedItemChanged, + {bool looping = false}) { + return CupertinoPicker( + key: UniqueKey(), + useMagnifier: false, + itemExtent: 80.rpx, + magnification: 1, + diameterRatio: 3, + squeeze: 1, + looping: looping, + scrollController: FixedExtentScrollController(initialItem: checkIndex), + selectionOverlay: Container(), + onSelectedItemChanged: (int value) { + // print("$value"); + onSelectedItemChanged.call(value); + }, + children: [ + ...List.generate(arr.length, (index) { + 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]}", + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx)), + ); + }) + ], + ); +} + +Future showDateSelectionDialog(BuildContext context, + {required DateTime checkDate, + Function? checkChange, + String title = "选择生日"}) { + Color checkColor = stringToColor("#D3B684"); + List years = [], months = [], days = []; + var days_select = [].obs; + int day_len = 31; + int year = DateTime.now().year; + for (var i = 0; i < 100; i++) { + years.insert(0, year - i); + } + for (var i = 1; i < 13; i++) { + months.add(i); + } + for (var i = 1; i < 32; i++) { + days.add(i); + } + int yearIndex = years.lastIndexOf(checkDate.year); + int monthIndex = months.lastIndexOf(checkDate.month); + day_len = DateTime.fromMillisecondsSinceEpoch( + DateTime(years[yearIndex], months[monthIndex] + 1) + .millisecondsSinceEpoch - + 1000) + .day; + days_select.value = days.sublist(0, day_len); + int dayIndex = days.lastIndexOf(checkDate.day); + return showDialog( + // barrierColor: stringToColor("#000320"), + 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: [ + Container( + alignment: Alignment.centerLeft, + height: 60.rpx, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "$title", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + 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: Container( + padding: EdgeInsets.only( + left: 40.rpx, right: 30.rpx), + child: getOnePicker(context, years, yearIndex, + (d) { + yearIndex = d; + dayIndex = 0; + day_len = DateTime.fromMillisecondsSinceEpoch( + DateTime(years[yearIndex], + months[monthIndex] + 1) + .millisecondsSinceEpoch - + 1000) + .day; + days_select.value = days.sublist(0, day_len); + }), + ), + ), + Container( + child: Text( + "年", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + Expanded( + child: Container( + padding: EdgeInsets.only( + left: 30.rpx, right: 30.rpx), + child: getOnePicker(context, months, monthIndex, + (d) { + monthIndex = d; + dayIndex = 0; + day_len = DateTime.fromMillisecondsSinceEpoch( + DateTime(years[yearIndex], + months[monthIndex] + 1) + .millisecondsSinceEpoch - + 1000) + .day; + days_select.value = days.sublist(0, day_len); + }), + ), + ), + Container( + child: Text( + "月", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + Expanded( + child: Container( + padding: EdgeInsets.only( + left: 30.rpx, right: 40.rpx), + child: Obx( + () { + // print("${dayIndex} ${day_len}"); + return getOnePicker( + context, + days_select, + dayIndex, + (d) { + dayIndex = d; + }, + ); + }, + ), + ), + ), + Container( + child: Text( + "日", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + ], + ), + ), + InkWell( + onTap: () { + checkChange?.call(DateTime(years[yearIndex], + months[monthIndex], days[dayIndex])); + 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: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + ) + ], + ), + ), + ), + ), + ), + ], + ); + }, + ); +} + +Future showDayTimeSelectionDialog(BuildContext context, + {required List dayTimeArr, Function? checkChange, String title = ""}) { + Color checkColor = stringToColor("#D3B684"); + List hours = [], minutes = []; + for (var i = 0; i < 24; i++) { + hours.add(i); + } + for (var i = 0; i < 60; i++) { + minutes.add(i); + } + int hoursIndex = hours.lastIndexOf(dayTimeArr[0]); + int minutesIndex = minutes.lastIndexOf(dayTimeArr[1]); + return showDialog( + // barrierColor: stringToColor("#000320"), + 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: [ + Container( + alignment: Alignment.centerLeft, + height: 60.rpx, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "$title", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + 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: Container( + padding: EdgeInsets.only( + left: 40.rpx, right: 30.rpx), + child: getOnePicker(context, hours, hoursIndex, + (d) { + hoursIndex = d; + }), + ), + ), + Container( + child: Text( + "时", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + Expanded( + child: Container( + padding: EdgeInsets.only( + left: 30.rpx, right: 30.rpx), + child: getOnePicker( + context, minutes, minutesIndex, (d) { + minutesIndex = d; + }), + ), + ), + Container( + child: Text( + "分", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + ], + ), + ), + InkWell( + onTap: () { + checkChange?.call( + [hours[hoursIndex], minutes[minutesIndex]]); + 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: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + ) + ], + ), + ), + ), + ), + ), + ], + ); + }, + ); +} + +Future showOneSelectionDialog(BuildContext context, + {required List arr, + int checkIndex = 0, + Function? checkChange, + String title = "选择性别"}) { + Color checkColor = stringToColor("#D3B684"); + return showDialog( + // barrierColor: stringToColor("#000320"), + 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: [ + Container( + alignment: Alignment.centerLeft, + height: 60.rpx, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "$title", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + closeIconWhite + ], + ), + ), + Container( + height: 240.rpx, + alignment: Alignment.center, + margin: EdgeInsets.only(top: 60.rpx, bottom: 60.rpx), + padding: EdgeInsets.only(left: 30.rpx, right: 30.rpx), + child: Container( + width: 400.rpx, + child: + getOnePicker(context, arr, checkIndex, (index) { + checkIndex = index; + }), + )), + InkWell( + onTap: () { + checkChange?.call(checkIndex); + 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: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + ) + ], + ), + ), + ), + ), + ), + ], + ); + }, + ); +} + +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 { + 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: Colors.white, fontSize: 30.rpx), + ), + ), + ), + ) + ], + ), + ), + ); + }, + ); +} + + +Future showCustomConfirmAndCancelDialog(BuildContext context, String name, + {String confirmName = "确定", + String cancelName = "取消", + ConfirmDialogIcon icon = ConfirmDialogIcon.warn}) async { + 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: Colors.white, fontSize: 30.rpx), + ), + ), + ) + ], + )), + ) + ], + ), + ), + ); + }, + ); +} + +//权限说明弹窗 +void showPermissionInfoDialog(BuildContext context, List data) { + 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: Colors.white, + 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, + ), + ], + ); + }), + ], + ), + ), + ), + ), + ), + ), + ], + ); + }, + ); +} diff --git a/lib/pages/device_bind/bind_device_success.dart b/lib/pages/device_bind/bind_device_success.dart new file mode 100644 index 0000000..626e40d --- /dev/null +++ b/lib/pages/device_bind/bind_device_success.dart @@ -0,0 +1,320 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.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/tool/CustomCard.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; + +class BindDeviceSuccess extends StatefulWidget { + const BindDeviceSuccess({super.key}); + + @override + State createState() => _EPageState(); +} + +class _EPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: stringToColor("#242835"), + // backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: Colors.white), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + // color: Colors.grey, + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '绑定成功.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(113.rpx, 0, 113.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 74.rpx, 0, 0), + child: Container( + width: 124.rpx, + height: 124.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/tick.svg', + fit: BoxFit.cover, + color: Colors.white, + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 42.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Text( + '绑定成功.绑定成功'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 48.rpx, + letterSpacing: 0.0, + color: Colors.white, + ), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 265.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Text( + '绑定成功.分享标题'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: Colors.white, + ), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 48.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: Text( + '绑定成功.分享内容'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: Colors.white, + ), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 110.rpx, 0, 0), + child: CustomCard( + borderRadius: + AppConstants().button_container_radius, // 圆角半径 + onTap: () { + print('Button pressed ...'); + Get.toNamed("/deviceType"); + }, + colors: [ + // 渐变色 + stringToColor("45D989"), // 左侧渐变色 + stringToColor("00C1AA"), // 右侧渐变色 + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.66, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/img/icon/share.svg', + width: 25.rpx, + height: 25.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: Colors.white, // 设置图标颜色为白色 + ), + Text( + '绑定成功.立即分享'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: Colors.white, // 文字颜色 + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, // 自定义字体大小 + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 18.rpx, 0, 0), + child: CustomCard( + borderRadius: + AppConstants().button_container_radius, // 圆角半径 + onTap: () { + Get.offAllNamed("/mianPageBottomChange"); + }, + colors: [ + // 渐变色 + stringToColor("45D989"), // 左侧渐变色 + stringToColor("00C1AA"), // 右侧渐变色 + ], + + child: Container( + width: MediaQuery.sizeOf(context).width * 0.66, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '绑定成功.返回'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: Colors.white, // 文字颜色 + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, // 自定义字体大小 + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required String type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == '1') { + Get.toNamed("/blueteethDevice"); + } + } + }, + // colors: [Colors.white.withOpacity(0.06)], // 背景色 + colors: [stringToColor("#242835")], // 背景色 + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.asset( + imageUrl, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/device_bind/blueteeth_device_page.dart b/lib/pages/device_bind/blueteeth_device_page.dart new file mode 100644 index 0000000..d653d24 --- /dev/null +++ b/lib/pages/device_bind/blueteeth_device_page.dart @@ -0,0 +1,1059 @@ +import 'dart:async'; + +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:permission_handler/permission_handler.dart'; // 引入permission_handler +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/model/BleDeviceData.dart'; +import 'package:vbvs_app/pages/common/selectDialog.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart'; +import 'package:vbvs_app/common/util/Ble.dart' as ble; + +class BlueteethDevicePage extends StatefulWidget { + int tid = -1; + BlueteethDevicePage({super.key, this.tid = -1}); + + @override + State createState() => _EPageState(); +} + +class _EPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + late FlutterBluePlus flutterBlue; // 声明 flutterBlue 实例 + List scanResults = []; // 存储扫描到的设备 + bool isScanning = false; // 扫描状态控制 + Timer? _timer; // 定时器,用于重复扫描 + bool _isDialogShowing = false; + + var currentConnectedDeviceProp; + var connectDeviceCurrent = null; + List bleDevice = []; + String currentMsg = "寻找设备中..."; + Timer? connectTimer; + bool isFind = false; + List bindArrBackup = []; + List bindArr = ["", "", ""]; + + @override + void initState() { + super.initState(); + flutterBlue = FlutterBluePlus(); // 初始化flutterBlue实例 + _checkBluetoothPermission(); // 检查蓝牙权限 + } + + // 检查蓝牙权限 + Future _checkBluetoothPermission() async { + PermissionStatus bluetoothStatus = await Permission.bluetooth.status; + PermissionStatus locationStatus = await Permission.location.status; + + if (bluetoothStatus.isGranted && locationStatus.isGranted) { + // 权限已授予,开始扫描 + _startScanning(); + _startPeriodicScan(); // 开始定时扫描 + } else { + // 权限未授予,请求权限 + _requestBluetoothPermission(); + } + } + + Future _requestBluetoothPermission() async { + // Android 13+ 使用新的蓝牙权限 + Map statuses = await [ + Permission.bluetoothScan, + Permission.bluetoothConnect, + Permission.location, // Android 12 及以下仍需要位置权限来进行扫描 + ].request(); + + bool allGranted = statuses[Permission.bluetoothScan]?.isGranted == true && + statuses[Permission.bluetoothConnect]?.isGranted == true && + statuses[Permission.location]?.isGranted == true; + + if (allGranted) { + // 用户授予了权限,开始扫描 + _startScanning(); + _startPeriodicScan(); + } else { + // 权限被拒绝,提示用户 + _showPermissionDeniedDialog(); + } + } + + // 显示权限被拒绝的提示 + void _showPermissionDeniedDialog() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("权限提示"), + content: Text("应用需要蓝牙和位置权限才能扫描设备。请授予权限。"), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text("确定"), + ), + ], + ); + }, + ); + } + + // 开始扫描蓝牙设备 + void _startScanning() async { + var bluetoothState = await FlutterBluePlus.isOn; + if (!bluetoothState && !_isDialogShowing) { + _isDialogShowing = true; + await _showBluetoothNotEnabledDialog(); + _isDialogShowing = false; + return; + } + + if (!isScanning) { + setState(() { + isScanning = true; + }); + + await FlutterBluePlus.startScan(timeout: Duration(seconds: 10)); + + // FlutterBluePlus.scanResults.listen((results) { + // // 使用 controller 中设定的最小信号强度过滤 + // final signalThreshold = blueteethBindController.model.singal!; + // final filteredResults = results + // .where((r) => + // r.rssi > signalThreshold && + // r.advertisementData.localName == "AITH-V2") + // .toList(); + // setState(() { + // scanResults = filteredResults; + // print("过滤后设备数量:${filteredResults.length}"); + // blueteethBindController.model.devicelist = filteredResults; + // blueteethBindController.updateAll(); + // if (filteredResults.isNotEmpty) { + // for (var r in filteredResults) { + // print("设备名: ${r.device.name}, RSSI: ${r.rssi}"); + // } + // } else { + // print("没有找到符合条件的设备"); + // } + // }); + // }); + + FlutterBluePlus.scanResults.listen((results) { + final signalThreshold = blueteethBindController.model.singal!; + final filteredResults = results + .where((r) => + r.rssi > signalThreshold && + r.advertisementData.localName == "AITH-V2" && + r.advertisementData.manufacturerData.containsKey(0xFFED)) + .toList(); + + // 解析数据 + final parsedDeviceList = []; + + for (var r in filteredResults) { + try { + List rawData = r.advertisementData.manufacturerData[0xFFED]!; + BleDeviceData deviceData = parseBleData(rawData); + deviceData.name = r.advertisementData.advName; + deviceData.rssi = r.rssi; + deviceData.mac = deviceData.deviceId.replaceAll(':', ''); + parsedDeviceList.add(deviceData); + } catch (e) { + print("设备数据解析失败: $e"); + } + } + + setState(() { + scanResults = filteredResults; + + // 存入 controller 中 + blueteethBindController.model.blelist = filteredResults; + blueteethBindController.model.devicelist = parsedDeviceList; + blueteethBindController.updateDeviceStatus(); + blueteethBindController.updateAll(); + + // print("过滤并解析后设备数量:${parsedDeviceList.length}"); + // for (var device in parsedDeviceList) { + // print( + // "设备: ${device.deviceId}, HR: ${device.ht}, BRE: ${device.bre}, Seq: ${device.fullSeq}"); + // } + + // if (parsedDeviceList.isEmpty) { + // print("没有符合条件的 BLE 广播数据"); + // } + }); + }); + + // 等待扫描完成 + await Future.delayed(Duration(seconds: 10)); + await FlutterBluePlus.stopScan(); + + setState(() { + isScanning = false; + }); + + print("扫描完成"); + } + } + + // 定时每10秒进行一次扫描 + void _startPeriodicScan() { + _timer = Timer.periodic(Duration(seconds: 10), (timer) { + if (!isScanning) { + _startScanning(); // 调用扫描函数 + } + }); + } + + // 停止扫描 + void _stopScanning() { + if (isScanning) { + FlutterBluePlus.stopScan(); // 停止扫描 + setState(() { + isScanning = false; // 更新扫描状态 + }); + } + } + + // 停止定时扫描 + void _stopPeriodicScan() { + _timer?.cancel(); + } + + @override + void dispose() { + super.dispose(); + _stopPeriodicScan(); // 停止定时扫描 + _stopScanning(); // 确保离开页面时停止扫描 + } + + connectToDevice(device, {int time = 5}) { + ble.connectToDevice( + { + "device": device, + 'success': (ble.ConnectedDeviceProp deviceProp) { + if (deviceProp.connectedDevicePropType == + ble.ConnectedDevicePropType.JunHe) { + currentConnectedDeviceProp = deviceProp; + deviceProp.write3OfString("blog enable"); + deviceProp.write3OfString("blog rlmax=128"); + Timer(const Duration(microseconds: 100), () async { + String log = ""; + Function logAdd = (l) { + log += l; + }; + deviceProp.receiveLogArr.add(logAdd); + deviceProp.encodeType = 1; + deviceProp.deviceType = 1; + Timer.periodic(const Duration(milliseconds: 300), (timer) async { + if (timer.tick > 20) { + ble.disconnect(currentConnectedDeviceProp); + failSelectDialog(); + timer.cancel(); + } + if (log.contains("GB2312") || log.contains("UTF-8")) { + timer.cancel(); + if (log.contains('CHARSET:UTF-8')) { + deviceProp.encodeType = 2; + } + if (log.contains('TARGET:ESPXX')) { + deviceProp.deviceType = 2; + } + log = ""; + bool isSuccess = false; + for (var i = 0; i < 4; i++) { + deviceProp.write3OfString("at+system info"); + await Future.delayed(const Duration(milliseconds: 400)); + RegExp regExp = RegExp(r"Target Mac:(\S*)"); + RegExpMatch? regExpMatch = regExp.firstMatch(log); + if (regExpMatch != null && regExpMatch.group(1) != null) { + String? mac = regExpMatch.group(1); + if (mac?.length == 12 && mac != "000000000000") { + bindArr[2] = "$mac".toUpperCase(); + } + isSuccess = true; + break; + } + } + deviceProp.receiveLogArr.remove(logAdd); + print("$bindArr"); + RegExp regExp = RegExp( + r"WIFI CONNECTED INFO:SSID=([^\t\n]*)\s*,RSSI=(\S*)\s*,"); + RegExpMatch? regExpMatch = regExp.firstMatch(log); + if (regExpMatch != null && log.contains("Status=connect")) { + blueteethBindController.model.connectedWifiName = + regExpMatch.group(1) ?? ""; + if (int.tryParse("${regExpMatch.group(2)}") != null) { + blueteethBindController.model.connectedRssi = + int.parse("${regExpMatch.group(2)}"); + } + blueteethBindController.updateAll(); + } + ble.bleParse(); + if (bindArr[0] != null && + bindArr[0] != "" && + bindArr[1] != "") { + setState(() { + currentMsg = "绑定中..."; + }); + blueteethBindController.bindDevice({ + "tid": widget.tid, + "name": blueteethBindController.model.deviceName, + "mac": bindArr[0], + "macA": bindArr[1], + "macB": bindArr[2] + }).then((d) { + blueteethBindController.model.bindArr = bindArr; + globalController.getDeviceList(); + LoadingDialog.hide(); + showCustomConfirmDialog(context, "设备添加成功!", + btnName: "打开WIFI配置", + icon: ConfirmDialogIcon.success) + .then((d) { + if (d == "confirm") { + Get.offAndToNamed("/wifi", arguments: deviceProp); + } + }); + }).catchError((d) { + print("$d"); + currentMsg = "绑定失败: ${d.message}"; + ble.disconnect(currentConnectedDeviceProp); + failSelectDialog(title: "${d.message}"); + }); + } else { + LoadingDialog.hide(); + Get.offAndToNamed("/wifi", arguments: deviceProp); + } + } else { + deviceProp.read6(); + } + }); + }); + } else if (deviceProp.connectedDevicePropType == + ble.ConnectedDevicePropType.QuanShi) { + List receive = []; + Function fun = (d) { + receive.add(d); + }; + deviceProp.receiveLogArr.add(fun); + List head = [ + 255, + 255, + 255, + 255, + 1, + 0, + 12, + 17, + ]; + Timer.periodic(const Duration(seconds: 1), (timer) { + if (timer.tick > 20) { + timer.cancel(); + currentMsg = "错误:未能获取到MAC"; + failSelectDialog(); + } + deviceProp.write( + Uint8List.fromList([ + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x01, + 0x00, + 0x0C, + 0x0B, + 0x0F, + 0x23, + 0x04 + ]), + null, + null); + if (receive.length > 0) { + receive.forEach((data) { + if (data.length != 17) { + return; + } + bool r = true; + for (var i = 0; i < head.length; i++) { + if (head[i] != data[i]) { + r = false; + } + } + if (r == false) { + return; + } + bindArr[1] = ble.ab2str(data.sublist(9, 15)).toUpperCase(); + timer.cancel(); + deviceProp.receiveLogArr.remove(fun); + blueteethBindController.model.deviceName = + deviceProp.connectDevice?.advName; + ble.disconnect(deviceProp); + toFindJunhe(); + }); + } + }); + } else { + List receive = []; + Function fun = (d) { + receive.add(d); + }; + deviceProp.receiveLogArr.add(fun); + List head = [255, 255, 255, 255, 0x00, 0x08, 0x40, 0x01]; + Timer.periodic(const Duration(seconds: 1), (timer) { + if (timer.tick > 20) { + timer.cancel(); + currentMsg = "错误:未能获取到MAC"; + failSelectDialog(); + } + deviceProp.write( + Uint8List.fromList([ + 255, + 255, + 255, + 255, + 0x00, + 0x03, + 0x40, + 0x01, + 0x01, + 0x00, + 0x45, + 0xfd + ]), + null, + null); + if (receive.length > 0) { + receive.forEach((data) { + if (data.length != 17) { + return; + } + bool r = true; + for (var i = 0; i < head.length; i++) { + if (head[i] != data[i]) { + r = false; + } + } + if (r == false) { + return; + } + + bindArr[1] = ble.ab2str(data.sublist(8, 14)).toUpperCase(); + print("$bindArr"); + timer.cancel(); + deviceProp.receiveLogArr.remove(fun); + blueteethBindController.model.deviceName = + deviceProp.connectDevice?.advName; + ble.disconnect(deviceProp); + toFindJunhe(); + }); + } + }); + } + }, + 'fail': (e) { + print(e); + if (time > 0) { + connectToDevice(device, time: time - 1); + } else { + currentMsg = "蓝牙无法连接上设备"; + failSelectDialog(title: currentMsg); + } + } + }, + ); + } + + isBind() { + return !(blueteethBindController.model.bindArr[1]?.length == 12); + } + + failSelectDialog({String title = ""}) { + LoadingDialog.hide(); + setState(() {}); + showCustomConfirmAndCancelDialog( + context, title == "" ? (isBind() ? "绑定失败" : "连接失败") : title, + confirmName: "重试", cancelName: "返回") + .then((d) { + if (d == "confirm") { + if (connectDeviceCurrent != null) { + ble.bleParse(); + ble.start((List d) { + setState(() { + bleDevice = d; + }); + }, bleOnCall: () { + LoadingDialog.show("连接中...\n靠近设备2米内", + icon: + isBind() ? LoadingDialogIcon.ble : LoadingDialogIcon.wifi); + setState(() { + currentMsg = "连接设备中..."; + }); + connectToDevice(connectDeviceCurrent); + }); + } else { + bleExec(); + } + } else if (d == "cancel") { + Get.back(); + } + }); + } + + bleExec() { + ble.bleParse(); + connectTimer?.cancel(); + int index = 0; + bool isCloseLoadingDialog = false; + isFind = false; + blueteethBindController.model.bindArr = bindArrBackup; + bindArr = ["", "", ""]; + ble.start((List d) { + setState(() { + bleDevice = d; + }); + if (isBind()) { + if (isCloseLoadingDialog == false && + bleDevice.indexWhere((item) { + if (widget.tid == 1) { + return ble.isQuanShiDevice(item["name"]); + } else { + return ble.isMHTSWES(item["name"]); + } + }) != + -1) { + isCloseLoadingDialog = true; + LoadingDialog.hide(); + } + } + }, bleOnCall: () { + if (isBind()) { + LoadingDialog.show("搜索蓝牙设备中...\n请打开蓝牙开关、定位开关\n与设备距离在2米内", + icon: isBind() ? LoadingDialogIcon.ble : LoadingDialogIcon.wifi); + Timer.periodic(const Duration(seconds: 1), (t) { + if (t.tick > 15) { + t.cancel(); + isCloseLoadingDialog = true; + LoadingDialog.hide(); + showCustomConfirmAndCancelDialog(context, "未发现设备", + confirmName: "重试", cancelName: "返回") + .then((d) { + if (d == "confirm") { + bleExec(); + } else if (d == "cancel") { + Get.back(); + } + }); + } else { + if (isCloseLoadingDialog == true) { + t.cancel(); + } + } + }); + return; + } + LoadingDialog.show( + "${isBind() ? "绑定中...\n与设备距离在2米内" : "连接中...\n请打开蓝牙开关、定位开关\n与设备距离在2米内"}", + icon: isBind() ? LoadingDialogIcon.ble : LoadingDialogIcon.wifi); + connectTimer = Timer.periodic(const Duration(seconds: 1), (t) { + index++; + if (index > 15) { + connectTimer = null; + t.cancel(); + failSelectDialog(); + } + var d = bleDevice; + if (d != null && d.length > 0) { + if (isBind()) { + var deviceble = d.firstWhere((item) { + bool isFF = false; + if (widget.tid == 1) { + isFF = ble.isQuanShiDevice(item["name"]); + } else { + isFF = ble.isMHTSWES(item["name"]); + } + if (isFF) { + isFF = globalController.model.deviceList.indexWhere( + (d) => d["mac"] == item["adData"]["deviceId"]) == + -1 + ? true + : false; + } + return isFF; + }, orElse: () => null); + if (!isFind && deviceble != null) { + print("quanshidevice"); + isFind = true; + setState(() { + currentMsg = "连接设备中..."; + }); + t.cancel(); + connectToDevice(deviceble["device"]); + bindArr[0] = deviceble["adData"]["deviceId"]; + } + } else { + var deviceble = d.firstWhere( + (item) => + item["adData"]["deviceId"] == + blueteethBindController.model.bindArr[1], + orElse: () => null); + if (!isFind && deviceble != null) { + print("junhedevice"); + isFind = true; + t.cancel(); + setState(() { + currentMsg = "连接设备中..."; + }); + connectToDevice(deviceble["device"]); + bindArr[1] = deviceble["adData"]["deviceId"]; + } + } + } + }); + }); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, boxConstraints) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + iconTheme: IconThemeData(color: Colors.white), + backgroundColor: stringToColor("#242835"), + automaticallyImplyLeading: false, + titleSpacing: 0, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + '蓝牙绑定.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: Color(0xFF242835), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 30.rpx, 0, 30.rpx), + child: Text( + '蓝牙绑定.扫描蓝牙设备中…'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Color(0xFFE8EEF3), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + color: Color(0xFF242835), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 21.rpx, 5.rpx, 21.rpx, 5.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '最小信号强度', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Color(0xFFEEF2F5), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Expanded( + child: Obx(() { + return Slider( + activeColor: Color(0xFF1FCC9B), + inactiveColor: + FlutterFlowTheme.of(context).alternate, + min: -100, + max: 50, + value: blueteethBindController.model.singal!, + onChanged: (newValue) { + newValue = double.parse( + newValue.toStringAsFixed(0)); + blueteethBindController.model.singal = + newValue; + blueteethBindController.updateAll(); + }, + ); + }), + ), + Obx(() { + return Text( + '${blueteethBindController.model.singal!.toInt()}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Color(0xFFE4E8EB), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ); + }), + ].divide(SizedBox(width: 30.rpx)), + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 35.rpx, 0, 35.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 0.rpx, 0, 0), + child: Container( + width: 25.rpx, + height: 25.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/query.svg', + fit: BoxFit.cover, + color: Colors.black, + ), + ), + ), + Expanded( + child: Container( + width: 100.rpx, + height: 100.rpx, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + ), + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: TextFormField( + autofocus: false, + obscureText: false, + decoration: InputDecoration( + isDense: true, + labelStyle: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + hintText: '蓝牙绑定.搜索提示'.tr, + hintStyle: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of(context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of(context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + filled: true, + fillColor: + FlutterFlowTheme.of(context) + .secondaryBackground, + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + cursorColor: + FlutterFlowTheme.of(context) + .primaryText, + // validator: _model + // .textControllerValidator + // .asValidator(context), + ), + ), + ), + ), + ].divide(SizedBox(width: 6.rpx)), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 26.rpx, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + SizedBox( + height: 50.rpx, + child: VerticalDivider( + thickness: 2.rpx, + color: FlutterFlowTheme.of(context) + .alternate, + ), + ), + Text( + '搜索', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 26.rpx)), + ), + ), + ], + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 60.rpx, 0, 32.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(19.rpx, 0, 0, 0), + child: Text( + '匹配出的外围设备(${blueteethBindController.model.devicelist!.length})', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: Colors.white), + ), + ), + ), + ), + Expanded( + child: Container( + width: double.infinity, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + ...blueteethBindController.model.blelist! + .map((device) => + SingleBlueteethDeviceCompoentWidget( + // device: device, + bleDevice: device, + )) + .toList() + .divide(SizedBox(height: 30.rpx)) + .addToEnd(SizedBox(height: 30.rpx)), + ], + ), + ), + ), + ), + ].divide(SizedBox(height: 30.rpx)), + ), + ), + ), + ), + ), + ), + ); + } + + _showBluetoothNotEnabledDialog() async { + await showDialog( + context: context, + builder: (_) => AlertDialog( + title: Text("蓝牙未开启"), + content: Text("请先打开蓝牙再进行设备扫描"), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text("知道了"), + ), + ], + ), + ); + } + + toFindJunhe() { + bool isSuccess = false; + int i = 0; + Timer.periodic(const Duration(seconds: 1), (t) async { + i++; + if (isSuccess) { + return; + } + if (i > 8) { + if (!isSuccess) { + currentMsg = "错误:未找到关联设备"; + failSelectDialog(title: "绑定失败:未找到关联设备"); + } + t.cancel(); + return; + } + bleDevice.forEach((item) { + if (isSuccess) { + return; + } + if (item['adData']['deviceId'] == bindArr[1]) { + isSuccess = true; + t.cancel(); + setState(() { + currentMsg = "寻找关联设备中..."; + }); + connectToDevice(item["device"]); + } + }); + }); + } +} + +BleDeviceData parseBleData(List data) { + if (data.length < 18) { + throw Exception('BLE广播数据长度不足18字节'); + } + + int type = data[0]; + int sn = data[1]; + + // 设备唯一ID (6字节),格式化为 MAC 地址样式 + String deviceId = + List.generate(6, (i) => data[2 + i].toRadixString(16).padLeft(2, '0')) + .join(":") + .toUpperCase(); + + int bre = data[8]; + int ht = data[9]; + int active = data[10]; + int flag = data[11]; + + // version 是4字节 uint,大端字节序 + int version = + (data[12] << 24) | (data[13] << 16) | (data[14] << 8) | data[15]; + + // qsn 是2字节 ushort,大端字节序 + int qsn = (data[16] << 8) | data[17]; + + return BleDeviceData( + type: type, + sn: sn, + deviceId: deviceId, + bre: bre, + ht: ht, + active: active, + flag: flag, + version: version, + qsn: qsn, + ); +} diff --git a/lib/pages/device_bind/componnet/DoubleBlueteethDeviceCompoentWidget.dart b/lib/pages/device_bind/componnet/DoubleBlueteethDeviceCompoentWidget.dart new file mode 100644 index 0000000..4664d0c --- /dev/null +++ b/lib/pages/device_bind/componnet/DoubleBlueteethDeviceCompoentWidget.dart @@ -0,0 +1,294 @@ +import 'package:ef/base/widget/flutterflow/FlutterFlowTheme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; + +class DoubleBlueteethDeviceCompoentWidget extends StatefulWidget { + const DoubleBlueteethDeviceCompoentWidget({super.key}); + + @override + State createState() => + _DoubleBlueteethDeviceCompoentWidgetState(); +} + +class _DoubleBlueteethDeviceCompoentWidgetState + extends State { + @override + void setState(VoidCallback callback) { + super.setState(callback); + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: Color(0xFF242835), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 36.rpx, 0, 52.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Text( + 'AITH-V3', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF6FAFD), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '信号强度:-36dBm', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Text( + 'SN:12', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF5F9FD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 40.rpx)), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Text( + '蓝牙地址:48:CA:43:B1:B3:B2', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Text( + 'mac:48CA43B1B3B0', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '网络:在线', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '传感器:', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Text( + '可绑定', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFF1AD2B5), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ], + ), + ].divide(SizedBox(width: 145.rpx)), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Text( + '版本:02409.0301', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + Divider( + thickness: 2.rpx, + color: FlutterFlowTheme.of(context).alternate, + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Text( + 'AITH-V3', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF6FAFD), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '信号强度:-36dBm', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Text( + 'SN:12', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF5F9FD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 40.rpx)), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Text( + '蓝牙地址:48:CA:43:B1:B3:B2', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Text( + 'mac:48CA43B1B3B0', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '网络:在线', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '传感器:', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Text( + '可绑定', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFF1AD2B5), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ], + ), + ].divide(SizedBox(width: 145.rpx)), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Text( + '版本:02409.0301', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + ].divide(SizedBox(height: 37.rpx)), + ), + ), + ); + } +} diff --git a/lib/pages/device_bind/componnet/FancyCircleCheckbox.dart b/lib/pages/device_bind/componnet/FancyCircleCheckbox.dart new file mode 100644 index 0000000..fec9480 --- /dev/null +++ b/lib/pages/device_bind/componnet/FancyCircleCheckbox.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; + +class FancyCircleCheckbox extends StatefulWidget { + final bool value; + final ValueChanged onChanged; + final Color borderColor; + final Color fillColor; + + const FancyCircleCheckbox({ + Key? key, + required this.value, + required this.onChanged, + this.borderColor = Colors.white, + this.fillColor = Colors.blue, + }) : super(key: key); + + @override + State createState() => _FancyCircleCheckboxState(); +} + +class _FancyCircleCheckboxState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _scaleAnimation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: Duration(milliseconds: 200), + vsync: this, + ); + _scaleAnimation = CurvedAnimation( + parent: _controller, + curve: Curves.easeInOut, + ); + + if (widget.value) { + _controller.forward(); + } + } + + @override + void didUpdateWidget(covariant FancyCircleCheckbox oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.value != oldWidget.value) { + widget.value ? _controller.forward() : _controller.reverse(); + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => widget.onChanged(!widget.value), + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: widget.value + ? widget.borderColor + : widget.borderColor.withOpacity(0.5), + width: 1, + ), + ), + child: ScaleTransition( + scale: _scaleAnimation, + child: Container( + margin: EdgeInsets.all(5), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: widget.fillColor, + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart b/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart new file mode 100644 index 0000000..84dab75 --- /dev/null +++ b/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart @@ -0,0 +1,184 @@ +import 'package:ef/base/widget/flutterflow/FlutterFlowTheme.dart'; +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/model/BleDeviceData.dart'; +import 'package:vbvs_app/pages/device_bind/blueteeth_device_page.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; + +class SingleBlueteethDeviceCompoentWidget extends StatefulWidget { + // final BleDeviceData device; + final bleDevice; + + const SingleBlueteethDeviceCompoentWidget({ + super.key, + // required this.device, + required this.bleDevice, + }); + + @override + State createState() => + _SingleBlueteethDeviceCompoentWidgetState(); +} + +class _SingleBlueteethDeviceCompoentWidgetState + extends State { + @override + Widget build(BuildContext context) { + var bleDevice = widget.bleDevice; + List rawData = + widget.bleDevice.advertisementData.manufacturerData[0xFFED]!; + BleDeviceData deviceData = parseBleData(rawData); + deviceData.name = widget.bleDevice.advertisementData.advName; + deviceData.rssi = widget.bleDevice.rssi; + deviceData.mac = deviceData.deviceId.replaceAll(':', ''); + BleDeviceData device = deviceData; + + ThemeController themeController = Get.find(); + return ClickableContainer( + backgroundColor: themeController.currentColor.sc5, + highlightColor: Colors.white, + borderRadius: 20.rpx, + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 36.rpx, 0, 52.rpx), + onTap: () async { + //todo 请求绑定 + print(device.mac); + // try { + // await bleDevice.device.connect(autoConnect: false); + // List services = + // await bleDevice.device.discoverServices(); + // BluetoothCharacteristic? wifiListChar; + // BluetoothCharacteristic? wifiSsidChar; + // BluetoothCharacteristic? wifiPasswordChar; + // BluetoothCharacteristic? startProvisionChar; + + // for (BluetoothService service in services) { + // for (BluetoothCharacteristic c in service.characteristics) { + // if (c.uuid.toString() == 'YOUR_WIFI_LIST_UUID') { + // wifiListChar = c; + // } else if (c.uuid.toString() == 'YOUR_WIFI_SSID_UUID') { + // wifiSsidChar = c; + // } else if (c.uuid.toString() == 'YOUR_WIFI_PWD_UUID') { + // wifiPasswordChar = c; + // } else if (c.uuid.toString() == 'YOUR_WIFI_START_UUID') { + // startProvisionChar = c; + // } + // } + // } + // } catch (e) { + // print("蓝牙连接失败"); + // } + // + // Get.toNamed('/wifiPage', arguments: bleDevice.device); + showBindDoubleDialog(context,[]); + // showHaveBindDialog(context); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + device.name ?? '蓝牙绑定.默认设备名称'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF6FAFD), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + Row( + children: [ + Text( + "蓝牙绑定.信号强度".tr + ':${device.rssi ?? '-'}dBm', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + SizedBox(width: 40.rpx), + Text( + "蓝牙绑定.SN".tr + ':${device.sn ?? '-'}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF5F9FD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ], + ), + Text( + "蓝牙绑定.蓝牙地址".tr + ':${device.deviceId ?? '-'}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Text( + "蓝牙绑定.mac".tr + ':${device.mac ?? '-'}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Row( + children: [ + Text( + "蓝牙绑定.网络".tr + + ':${device.isOnline == true ? '蓝牙绑定.在线'.tr : '蓝牙绑定.离线'.tr}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + SizedBox(width: 145.rpx), + Row( + children: [ + Text( + "蓝牙绑定.传感器".tr + ":", + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFEBF2F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + Text( + device.status == 1 ? '蓝牙绑定.可绑定'.tr : '蓝牙绑定.已被绑定'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFF1AD2B5), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ], + ), + ], + ), + Text( + '版本:${device.version ?? '-'}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFF6FAFD), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(height: 37.rpx)), + ), + ); + } +} diff --git a/lib/pages/device_bind/componnet/bind_dialog.dart b/lib/pages/device_bind/componnet/bind_dialog.dart new file mode 100644 index 0000000..3db176f --- /dev/null +++ b/lib/pages/device_bind/componnet/bind_dialog.dart @@ -0,0 +1,324 @@ +import 'dart:ui'; + +import 'package:ef/ef.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/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/FrostedDialog.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/model/BleDeviceData.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/FancyCircleCheckbox.dart'; + +void showBindDoubleDialog(BuildContext context, List devices) { + ThemeController themeController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + + blueteethBindController.model.deviceIndex0 = true; + blueteethBindController.model.deviceIndex1 = false; + blueteethBindController.model.deviceIndex2 = false; + + showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), // 建议加个背景模糊色 + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(60.rpx, 0, 60.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 标题 + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 93.rpx, 0, 0), + child: Text( + '蓝牙绑定.双人版绑定标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + // 全选 + _buildCheckboxRow( + context, + title: '蓝牙绑定.绑定全部'.tr, + value: () => blueteethBindController.model.deviceIndex0!, + onChanged: (v) { + if (!blueteethBindController.model.deviceIndex0!) { + blueteethBindController.model.deviceIndex0 = v; + blueteethBindController.model.deviceIndex1 = !v; + blueteethBindController.model.deviceIndex2 = !v; + blueteethBindController.updateAll(); + } + }, + ), + // 主设备 + _buildCheckboxRow( + context, + title: '蓝牙绑定.主设备'.tr, + value: () => blueteethBindController.model.deviceIndex1!, + onChanged: (v) { + if (!blueteethBindController.model.deviceIndex1!) { + blueteethBindController.model.deviceIndex1 = v; + blueteethBindController.model.deviceIndex0 = !v; + blueteethBindController.model.deviceIndex2 = !v; + blueteethBindController.updateAll(); + } + }, + ), + // 从设备 + _buildCheckboxRow( + context, + title: '蓝牙绑定.从设备'.tr, + value: () => blueteethBindController.model.deviceIndex2!, + onChanged: (v) { + if (!blueteethBindController.model.deviceIndex2!) { + blueteethBindController.model.deviceIndex2 = v; + blueteethBindController.model.deviceIndex0 = !v; + blueteethBindController.model.deviceIndex1 = !v; + blueteethBindController.updateAll(); + } + }, + ), + // 确定按钮 + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 100.rpx, 0, 0), + child: _buildActionButton( + context, + text: '蓝牙绑定.确定'.tr, + onTap: () { + Get.back(); + }, + ), + ), + // 取消按钮 + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 19.rpx, 0, 60.rpx), + child: _buildActionButton( + context, + text: '蓝牙绑定.取消'.tr, + onTap: () { + Get.back(); + }, + ), + ), + ], + ), + ), + ), + ); + }, + ); +} + + + +void showHaveBindDialog(BuildContext context) { + ThemeController themeController = Get.find(); + + showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(60.rpx, 0, 60.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 93.rpx, 0.rpx, 0.rpx), + child: Text( + '蓝牙绑定.无法绑定'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 70.rpx, 0.rpx, 56.rpx), + child: RichText( + text: TextSpan(children: [ + TextSpan( + text: "蓝牙绑定.无法绑定1".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().normal_text_fontSize, + ), + ), + TextSpan( + text: "蓝牙绑定.无法绑定2".tr, + style: TextStyle( + color: themeController.currentColor.sc10, + fontSize: AppConstants().normal_text_fontSize, + ), + ), + TextSpan( + text: "蓝牙绑定.无法绑定3".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: AppConstants().normal_text_fontSize, + ), + ), + ]), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 19.rpx, 0, 60.rpx), + child: CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + Get.back(); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '蓝牙绑定.知道了'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: Colors.white, + fontFamily: 'Inter', + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox( + width: 17.rpx, + )), + ), + ), + ), + ), + ], + ), + ), + ), + ); + }, + ); +} + + +Widget _buildCheckboxRow( + BuildContext context, { + required String title, + required bool Function() value, + required void Function(bool) onChanged, +}) { + ThemeController themeController = Get.find(); + return Padding( + padding: EdgeInsetsDirectional.fromSTEB(60.rpx, 64.rpx, 0.rpx, 0.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Obx(() => FancyCircleCheckbox( + borderColor: themeController.currentColor.sc3, + fillColor: themeController.currentColor.sc2, + value: value(), + onChanged: onChanged, + )), + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + letterSpacing: 0.0, + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ].divide(SizedBox(width: 21.rpx)), + ), + ); +} + +Widget _buildActionButton( + BuildContext context, { + required String text, + required VoidCallback onTap, +}) { + ThemeController themeController = Get.find(); + return CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: onTap, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + text, + style: FlutterFlowTheme.of(context).bodyMedium.override( + color: Colors.white, + fontFamily: 'Inter', + fontSize: AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ); +} diff --git a/lib/pages/device_bind/device_type.dart b/lib/pages/device_bind/device_type.dart index 48a52e1..b13fd5f 100644 --- a/lib/pages/device_bind/device_type.dart +++ b/lib/pages/device_bind/device_type.dart @@ -1,12 +1,15 @@ import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.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/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/FrostedDialog.dart'; import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; class DeviceTypePage extends StatefulWidget { @@ -20,6 +23,7 @@ class _EPageState extends State { GlobalController globalController = Get.find(); UserInfoController userInfoController = Get.find(); BlueteethBindController blueteethBindController = Get.find(); + ThemeController themeController = Get.find(); @override void initState() { @@ -31,79 +35,84 @@ class _EPageState extends State { context: context, barrierDismissible: false, builder: (BuildContext context) { - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20.0), - ), - backgroundColor: Colors.transparent, + return FrostedDialog( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), - child: Container( - width: double.infinity, - constraints: BoxConstraints( - maxHeight: MediaQuery.sizeOf(context).height * 0.656, - ), - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Container( - width: double.infinity, - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Container( + width: double.infinity, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 26.rpx, 0, 0), + child: Container( + width: MediaQuery.sizeOf(context).width, + decoration: BoxDecoration( + color: Color(0xFFFBF5D5), + borderRadius: BorderRadius.circular( + AppConstants() + .normal_container_radius), // 圆角半径 + ), + child: Padding( padding: EdgeInsetsDirectional.fromSTEB( - 0, 26.rpx, 0, 0), - child: Container( - width: MediaQuery.sizeOf(context).width, - decoration: BoxDecoration( - color: Color(0xFFFBF5D5), - borderRadius: BorderRadius.circular( - AppConstants() - .normal_container_radius), // 圆角半径 - ), - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 25.rpx, 25.rpx, 25.rpx, 25.rpx), - child: Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Icon( - Icons.volume_mute, - color: - FlutterFlowTheme.of(context) - .primaryText, - size: 30.rpx, + 25.rpx, 25.rpx, 25.rpx, 25.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0, 8.rpx, 0, 0), + child: Container( + width: 25.rpx, + height: 25.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/sound.svg', + fit: BoxFit.fill, + color: themeController + .currentColor.sc8, ), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - '绑定引导.说明标题'.tr, - style: FlutterFlowTheme.of( - context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: AppConstants() - .normal_text_fontSize, - letterSpacing: 0.0, - fontWeight: - FontWeight.w500, - color: - Colors.orange), - ), - Text( - '绑定引导.说明正文'.tr, - style: FlutterFlowTheme.of( - context) + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '绑定引导.说明标题'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + fontWeight: + FontWeight.w500, + color: Colors.orange), + ), + Text( + '绑定引导.说明正文'.tr, + style: + FlutterFlowTheme.of(context) .bodyMedium .override( fontFamily: 'Inter', @@ -111,162 +120,161 @@ class _EPageState extends State { .normal_text_fontSize, letterSpacing: 0.0, ), - ), - ].divide(SizedBox( - height: AppConstants() - .text_padding_up_dowm_p)), ), - ), - ].divide(SizedBox(width: 20.rpx)), + ].divide(SizedBox( + height: AppConstants() + .text_padding_up_dowm_p)), + ), ), - ), + ].divide(SizedBox(width: 20.rpx)), ), ), - Container( - width: double.infinity, - height: (MediaQuery.sizeOf(context).width) * - 0.13, - constraints: BoxConstraints( - minHeight: 200.rpx, - ), - child: ClipRRect( - borderRadius: - BorderRadius.circular(20.rpx), - // child: Image.network( - // 'https://picsum.photos/seed/861/600', - // fit: BoxFit.cover, - // ), - child: Image.asset( - "assets/img/help_op.png", - fit: BoxFit.cover, - ), - ), - ), - ].divide(SizedBox(height: 25.rpx)), + ), ), - ), + Container( + width: double.infinity, + height: + (MediaQuery.sizeOf(context).width) * 0.13, + constraints: BoxConstraints( + minHeight: 200.rpx, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.rpx), + // child: Image.network( + // 'https://picsum.photos/seed/861/600', + // fit: BoxFit.cover, + // ), + child: Image.asset( + "assets/img/help_op.png", + fit: BoxFit.cover, + ), + ), + ), + ].divide(SizedBox(height: 25.rpx)), ), ), - Flexible( - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0, 210.rpx, 0, 0), - child: Container( - width: double.infinity, - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - width: double.infinity, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Theme( - data: ThemeData( - checkboxTheme: CheckboxThemeData( - visualDensity: - VisualDensity.compact, - materialTapTargetSize: - MaterialTapTargetSize - .shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(64), - ), - ), - unselectedWidgetColor: - Color(0xFFD3D3D3), + ), + ), + Flexible( + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 210.rpx, 0, 0), + child: Container( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: double.infinity, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Theme( + data: ThemeData( + checkboxTheme: CheckboxThemeData( + visualDensity: + VisualDensity.compact, + materialTapTargetSize: + MaterialTapTargetSize + .shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(64), ), - child: Obx( - () => Checkbox( - value: userInfoController - .model.register_agree ?? - false, - onChanged: (newValue) async { - userInfoController.model - .register_agree = - newValue; - userInfoController - .updateAll(); - // 获取设备信息,需要用户点击确认隐私协议与用户协议选择框时才能获取 - // if (newValue == true) { - // Deviceconfig - // .initPlatformState(); - // } - }, - side: BorderSide( - width: 1.5, - color: FlutterFlowTheme.of( - context) - .secondaryText, - ), - activeColor: - stringToColor("#16C89F"), - checkColor: - FlutterFlowTheme.of(context) - .info, - ), - )), - Text( - '绑定引导.不再提示'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: Colors.white), + ), + unselectedWidgetColor: + Color(0xFFD3D3D3), ), - ].divide(SizedBox(width: 22.rpx)), - ), - ), - CustomCard( - borderRadius: 50.rpx, - onTap: () async { - await Future.delayed( - Duration(milliseconds: 500)); - Get.back(); // 关闭当前弹窗或页面 - }, - colors: [ - //todo 颜色 - stringToColor("45D989"), - stringToColor("00C1AA") - ], // 单色背景也用渐变写法 - title: '', - child: Container( - width: MediaQuery.sizeOf(context).width * - 0.66, - height: - MediaQuery.sizeOf(context).height * - 0.055, - constraints: BoxConstraints( - minWidth: 500.rpx, - minHeight: 90.rpx, - ), - alignment: Alignment.center, // 居中对齐 - child: Text( - '绑定引导.跳过'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( + child: Obx( + () => Checkbox( + value: blueteethBindController + .model.read == + 1 + ? false + : true, + onChanged: (newValue) async { + blueteethBindController + .model.read = + newValue == true ? 0 : 1; + blueteethBindController + .updateAll(); + // 获取设备信息,需要用户点击确认隐私协议与用户协议选择框时才能获取 + // if (newValue == true) { + // Deviceconfig + // .initPlatformState(); + // } + }, + side: BorderSide( + width: 1.5, + color: + FlutterFlowTheme.of(context) + .secondaryText, + ), + activeColor: + stringToColor("#16C89F"), + checkColor: + FlutterFlowTheme.of(context) + .info, + ), + )), + Text( + '绑定引导.不再提示'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( fontFamily: 'Inter', fontSize: 26.rpx, letterSpacing: 0.0, - ), - ), + color: Colors.white), ), - ) - ].divide(SizedBox(height: 42.rpx)), + ].divide(SizedBox(width: 22.rpx)), + ), ), - ), + CustomCard( + borderRadius: 50.rpx, + onTap: () async { + await Future.delayed( + Duration(milliseconds: 500)); + Get.back(); // 关闭当前弹窗或页面 + }, + colors: [ + //todo 颜色 + stringToColor("45D989"), + stringToColor("00C1AA") + ], // 单色背景也用渐变写法 + + child: Container( + width: + MediaQuery.sizeOf(context).width * 0.66, + height: + MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + alignment: Alignment.center, // 居中对齐 + child: Text( + '绑定引导.跳过'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + ) + ].divide(SizedBox(height: 42.rpx)), ), ), - ], + ), ), - ), - )); + ], + ), + ), + )); }, ); } @@ -275,7 +283,7 @@ class _EPageState extends State { @override Widget build(BuildContext context) { - int read = blueteethBindController.model.read; + int read = blueteethBindController.model.read!; if (blueteethBindController.model.read == 1) { //需要弹窗显示教程 } @@ -378,11 +386,12 @@ class _EPageState extends State { if (type == '1') { Get.toNamed("/blueteethDevice"); } + if (type == '2') { + Get.toNamed("/wifiPage"); + } } }, - // colors: [Colors.white.withOpacity(0.06)], // 背景色 colors: [stringToColor("#242835")], // 背景色 - title: title, child: Container( width: double.infinity, height: MediaQuery.sizeOf(context).height * 0.135, diff --git a/lib/pages/device_bind/wifi_page.dart b/lib/pages/device_bind/wifi_page.dart new file mode 100644 index 0000000..fe7801b --- /dev/null +++ b/lib/pages/device_bind/wifi_page.dart @@ -0,0 +1,502 @@ +import 'dart:async'; + +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/Ble.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/person/person_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/common/util/Ble.dart' as ble; +import 'package:vbvs_app/pages/common/selectDialog.dart'; + +class WifiPage extends StatefulWidget { + // ble.ConnectedDeviceProp connectedDeviceProp; + BluetoothDevice bluetoothDevice; + // WifiPage({super.key, required this.connectedDeviceProp}); + WifiPage({super.key, required this.bluetoothDevice}); + + @override + State createState() => _WifiPageState(); +} + +class _WifiPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + PersonController personController = Get.find(); + ThemeController themeController = Get.find(); + late ble.ConnectedDeviceProp connectedDeviceProp; + + @override + void initState() { + super.initState(); + connectToDevice(widget.bluetoothDevice); + // connectedDeviceProp = widget.connectedDeviceProp; + Timer(const Duration(microseconds: 100), () { + getWifiList(); + }); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: stringToColor("#242835"), + // backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: Colors.white), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + // color: Colors.grey, + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + 'wifi页.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtom, + ), + Positioned( + right: 20.rpx, + child: CustomCard( + borderRadius: 20.rpx, + onTap: () async { + Get.offAllNamed("/bindDeviceSuccess"); + }, + colors: [ + stringToColor("#45D989"), + stringToColor("#00C1AA"), + ], + child: Container( + width: 100.rpx, + height: 60.rpx, + alignment: Alignment.center, + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 0, 16.rpx, 0), + child: Text( + 'wifi页.跳过'.tr, + style: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Inter Tight', + color: Colors.white, + letterSpacing: 0.0, + ), + ), + ), + ), + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: Color(0xFF242835), + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "wifi页.WLAN".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: + AppConstants().title_text_fontSize, + ), + ), + Text( + "wifi页.未连接".tr, + style: TextStyle( + color: themeController.currentColor.sc3, + fontSize: + AppConstants().normal_text_fontSize, + ), + ), + ], + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 25.rpx, 0.rpx, 0.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '可用WLAN', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ], + ), + Column( + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + '6503', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Icon( + Icons.wifi_outlined, + size: 30.rpx, + color: themeController + .currentColor.sc3, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + '6503', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Icon( + Icons.wifi_outlined, + size: 30.rpx, + color: themeController + .currentColor.sc3, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + '6503', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Icon( + Icons.wifi_outlined, + size: 30.rpx, + color: themeController + .currentColor.sc3, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + '6503', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Icon( + Icons.wifi_outlined, + size: 30.rpx, + color: themeController + .currentColor.sc3, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + '6503', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Icon( + Icons.wifi_outlined, + size: 30.rpx, + color: themeController + .currentColor.sc3, + ), + ], + ), + ].divide(SizedBox(height: 67.rpx)), + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.arrow_back, + color: themeController.currentColor.sc3, + size: 30.rpx, + ), + Text( + '刷新', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3), + ), + ].divide(SizedBox(width: 26.rpx)), + ), + ].divide(SizedBox(height: 65.rpx)), + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required String type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == '1') { + Get.toNamed("/blueteethDevice"); + } + } + }, + // colors: [Colors.white.withOpacity(0.06)], // 背景色 + colors: [stringToColor("#242835")], // 背景色 + + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.asset( + imageUrl, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } + + getWifiList({int time = 3}) { + // LoadingDialog.show("扫描WIFI列表中...", icon: LoadingDialogIcon.wifi); + try { + var device = widget.bluetoothDevice; + String log = ""; + Function logAdd = (l) { + log += l; + }; + connectedDeviceProp.receiveLogArr.add(logAdd); + connectedDeviceProp.write3OfString("wscan scan", success: () { + Timer.periodic(const Duration(milliseconds: 1000), (timer) async { + if (timer.tick > 8) { + timer.cancel(); + connectedDeviceProp.receiveLogArr.remove(logAdd); + LoadingDialog.hide(); + if (time > 0) { + getWifiList(time: time - 1); + } + } + Iterable a = RegExp( + r'ITEM:SSID=([^\t]*)\s*RSSI=(\S*)\s*(,\s*auth\s*=\s*(\S*))?') + .allMatches(log); + if (a.isEmpty == false) { + await Future.delayed(const Duration(milliseconds: 500)); + a = RegExp( + r'ITEM:SSID=([^\t]*)\s*RSSI=(\S*)\s*(,\s*auth\s*=\s*(\S*))?') + .allMatches(log); + List arr = []; + for (RegExpMatch one in a) { + arr.add({"name": one[1], "num": one[2], "auth": one[4]}); + } + LoadingDialog.hide(); + blueteethBindController.model.wifiList = arr; + blueteethBindController.updateAll(); + connectedDeviceProp.receiveLogArr.remove(logAdd); + timer.cancel(); + checkIsCalibration(); + } + }); + }, fail: () { + connectedDeviceProp.receiveLogArr.remove(logAdd); + LoadingDialog.hide(); + }); + } catch (e) { + print(e); + } + } + + checkIsCalibration() { + // if (controller.model.bindArr[0] == "" || + // controller.model.bindArr[0] == null) { + // return; + // } + // if (controller.model.bindArr[2] == "" || + // controller.model.bindArr[2] == null) { + // return; + // } + // if (controller.model.connectedWifiName == "" || + // controller.model.connectedWifiName == null) { + // return; + // } + showCustomConfirmAndCancelDialog(context, "是否进行设备校准?", confirmName: "去校准") + .then((d) async { + // if (d == "confirm") { + // await Get.offAndToNamed("/calibration", arguments: [ + // controller.model.bindArr[1], + // controller.model.bindArr[2] + // ]); + // Get.find().getDeviceList(); + // } + }); + } +} diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index c27e3d1..923d8e1 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -7,6 +7,7 @@ import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/controller/login/login_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; @@ -20,6 +21,7 @@ class LoginPage extends StatefulWidget { class _EPageState extends State { GlobalController globalController = Get.find(); UserInfoController userInfoController = Get.find(); + LoginController loginController = Get.find(); @override Widget build(BuildContext context) { @@ -75,36 +77,6 @@ class _EPageState extends State { child: Column( mainAxisSize: MainAxisSize.max, children: [ - // ClickableContainer( - // backgroundColor: Colors.transparent, // 容器背景色为透明 - // highlightColor: Colors.pink, // 点击时背景色也为透明 - // padding: EdgeInsets.all(0), // 没有额外的内边距 - // onTap: () { - // // 你可以在这里定义点击事件的回调,比如关闭页面等 - // print('关闭按钮被点击'); - // }, - // borderRadius: 0, // 没有圆角 - // child: Container( - // // color: Colors.red, - // // width: double.infinity, // 使容器宽度充满父容器 - // child: Align( - // alignment: - // AlignmentDirectional(-1, 0), // 左对齐 - // child: Padding( - // padding: EdgeInsetsDirectional.fromSTEB( - // 0, 66.rpx, 0, 0), - // child: SvgPicture.asset( - // 'assets/img/icon/close.svg', - // width: 25.rpx, - // height: 25 - // .rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 - // color: Colors.white, // 这里设置了颜色 - // ), - // ), - // ), - // ), - // ), - Align( alignment: AlignmentDirectional(-1, 0), child: Padding( @@ -190,16 +162,20 @@ class _EPageState extends State { borderRadius: AppConstants() .button_container_radius, // 圆角半径 onTap: () { - print('Button pressed ...'); - // Get.toNamed("/deviceType"); + bool agree = loginController + .model.register_agree!; + if (!agree) { + print('未授权 ...'); + } else { + print('已授权 ...'); + } }, colors: [ //todo 颜色 stringToColor("45D989"), stringToColor("00C1AA") ], // 渐变色是同一个色,也可以根据需要调整 - title: - '首页.蓝牙绑定'.tr, // 可选,虽然这个 title 没用,但可以作为调试用 + child: Container( width: // MediaQuery.sizeOf(context).width * 0.66, @@ -215,13 +191,6 @@ class _EPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ - // Icon( - // Icons.arrow_back, - // color: FlutterFlowTheme.of(context) - // .primaryText, - // size: 28.rpx, - // ), - Text( '登录页.本机号码一键登录/注册'.tr, style: FlutterFlowTheme.of(context) @@ -242,39 +211,6 @@ class _EPageState extends State { ), ), ), - // ClickableContainer( - // backgroundColor: Colors.transparent, // 背景色(透明) - // highlightColor: stringToColor( - // "#FF6347"), // 点击时的背景色,可以根据需求设置颜色 - // padding: EdgeInsetsDirectional.fromSTEB( - // 0, 32.rpx, 0, 32.rpx), // 内部间距 - // onTap: () { - // print('点击了“其他手机号码”'); // 点击后的回调事件 - // // 这里可以放置点击后的逻辑,比如导航等 - // }, - // borderRadius: 0.rpx, // 可选的圆角参数,默认是 20.rpx - // child: Align( - // alignment: AlignmentDirectional(-1, 0), - // child: Container( - // width: double.infinity, - // decoration: BoxDecoration(), - // child: Align( - // alignment: AlignmentDirectional(0, 0), - // child: Text( - // '登录页.其他手机号码'.tr, - // style: FlutterFlowTheme.of(context) - // .bodyMedium - // .override( - // fontFamily: 'Inter', - // fontSize: 26.rpx, - // letterSpacing: 0.0, - // color: stringToColor("#FFFFFF"), - // ), - // ), - // ), - // ), - // ), - // ), SizedBox( height: 20.rpx, ), @@ -285,6 +221,7 @@ class _EPageState extends State { 16.rpx, 10.rpx, 16.rpx, 10.rpx), onTap: () { print('点击了容器'); + Get.toNamed("/otherLoginPage"); }, child: Text( '登录页.其他手机号码'.tr, // 子组件内容 @@ -306,14 +243,6 @@ class _EPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - // SvgPicture.asset( - // 'assets/img/icon/tick.svg', - // width: 30.rpx, - // height: 30.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - // //todo 颜色 - // // color: Colors.white, - // color: Colors.white, - // ), Theme( data: ThemeData( checkboxTheme: CheckboxThemeData( @@ -332,13 +261,13 @@ class _EPageState extends State { ), child: Obx( () => Checkbox( - value: userInfoController + value: loginController .model.register_agree ?? false, onChanged: (newValue) async { - userInfoController.model + loginController.model .register_agree = newValue; - userInfoController.updateAll(); + loginController.updateAll(); // 获取设备信息,需要用户点击确认隐私协议与用户协议选择框时才能获取 // if (newValue == true) { // Deviceconfig @@ -358,7 +287,6 @@ class _EPageState extends State { .info, ), )), - Expanded( child: Padding( padding: diff --git a/lib/pages/login/other_login.dart b/lib/pages/login/other_login.dart new file mode 100644 index 0000000..0a98278 --- /dev/null +++ b/lib/pages/login/other_login.dart @@ -0,0 +1,801 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.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/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/controller/login/login_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/time/countdown_controller.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; + +class OtherLoginPage extends StatefulWidget { + const OtherLoginPage({super.key}); + + @override + State createState() => _OtherLoginPageState(); +} + +class _OtherLoginPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + ThemeController themeController = Get.find(); + CountdownController countdownController = Get.find(); + LoginController loginController = Get.find(); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodysize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgImage.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + body: SafeArea( + top: true, + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(75.rpx, 0.rpx, 75.rpx, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + SizedBox( + height: 66.rpx, + ), + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: Colors.green, // 点击时的背景色 + padding: + EdgeInsets.zero, // 这里去掉外部的 padding,避免影响点击范围 + onTap: () { + Get.back(); + }, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 10.rpx, 16.rpx, 10.rpx), + child: SvgPicture.asset( + 'assets/img/icon/arrow_left.svg', + width: 25.rpx, + height: 25.rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 + color: Colors.white, // 这里设置了颜色 + ), + ), + ), + ], + ), + Expanded( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Align( + alignment: AlignmentDirectional(-1, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 141.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Text( + '登录页.欢迎使用太和e护'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 48.rpx, + letterSpacing: 0.0, + //todo 颜色 + color: stringToColor("#FFFFFF"), + ), + ), + ), + ), + ), + ), + Align( + alignment: AlignmentDirectional(-1, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 15.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Text( + '登录页.科技睡眠 洞悉万千'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + //todo 颜色 + color: stringToColor("#FFFFFF"), + ), + ), + ), + ), + ), + ), + Align( + alignment: AlignmentDirectional(-1, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 95.rpx, 0, 0), + child: Container( + width: double.infinity, + height: bodysize.maxHeight * 0.056, + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular( + AppConstants() + .button_container_radius), + border: Border.all( + color: Colors.white, + width: 1.rpx, + ), + ), + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 35.rpx, 0, 35.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + child: Align( + alignment: + AlignmentDirectional(-1, 0), + child: TextFormField( + autofocus: false, + obscureText: false, + decoration: InputDecoration( + isDense: true, + labelStyle: FlutterFlowTheme + .of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + hintText: '其他手机登录页.输入内容'.tr, + hintStyle: FlutterFlowTheme + .of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + enabledBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + errorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + filled: false, + fillColor: FlutterFlowTheme + .of(context) + .secondaryBackground, + ), + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + cursorColor: + FlutterFlowTheme.of( + context) + .primaryText, + // validator: _model + // .textControllerValidator + // .asValidator(context), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + Align( + alignment: AlignmentDirectional(-1, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 19.rpx, 0, 0), + child: Container( + height: bodysize.maxHeight * 0.056, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular( + AppConstants() + .button_container_radius), + border: Border.all( + color: Colors.white, + width: 1.rpx, + ), + ), + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 35.rpx, 0, 35.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + child: Align( + alignment: + AlignmentDirectional(-1, 0), + child: TextFormField( + autofocus: false, + obscureText: false, + decoration: InputDecoration( + isDense: true, + labelStyle: FlutterFlowTheme + .of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + hintText: + '其他手机登录页.输入验证码'.tr, + hintStyle: FlutterFlowTheme + .of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + enabledBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + errorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + filled: true, + fillColor: + Colors.transparent, + ), + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + cursorColor: + FlutterFlowTheme.of( + context) + .primaryText, + // validator: _model + // .textControllerValidator + // .asValidator(context), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 26.rpx, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + SizedBox( + height: 50.rpx, + child: VerticalDivider( + thickness: 2.rpx, + color: themeController + .currentColor.sc7, + ), + ), + // Text( + // countdownController.countdown + // .value == + // 0 + // ? '其他手机登录页.获取验证码'.tr + // : '${countdownController.countdown.value}秒', + // style: TextStyle( + // fontFamily: 'Readex Pro', + // color: themeController + // .currentColor.sc7, + // fontSize: AppConstants() + // .title_text_fontSize, + // letterSpacing: 0, + // ), + // ), + Obx(() { + final CountdownController + countdownController = + Get.find< + CountdownController>(); + return InkWell( + onTap: () async { + if (countdownController + .countdown + .value != + 0) { + return; + } + + String msg = + await loginController + .getCode(context); + if (msg.isNotEmpty) { + return; + } + countdownController + .countdown + .value == + 0 + ? countdownController + .startCountdown( + AppConstants + .code_time) + : null; + }, + child: Container( + alignment: + Alignment.center, + constraints: + BoxConstraints( + minWidth: 150.rpx, + ), + child: Text( + countdownController + .countdown + .value == + 0 + ? '获取验证码' + : '${countdownController.countdown.value}秒', + style: TextStyle( + fontFamily: + 'Readex Pro', + color: + Color(0xFF333333), + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0, + ), + ), + ), + ); + }), + ].divide(SizedBox(width: 26.rpx)), + ), + ), + ], + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 35.rpx, 0, 0), + child: CustomCard( + borderRadius: AppConstants() + .button_container_radius, // 圆角半径 + onTap: () { + bool agree = + loginController.model.register_agree!; + if (!agree) { + print('未授权 ...'); + } else { + print('已授权 ...'); + } + }, + colors: [ + //todo 颜色 + stringToColor("45D989"), + stringToColor("00C1AA") + ], // 渐变色是同一个色,也可以根据需要调整 + + child: Container( + width: + // MediaQuery.sizeOf(context).width * 0.66, + bodysize.maxWidth, + height: MediaQuery.sizeOf(context).height * + 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Text( + '其他手机登录页.登录'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + //todo 颜色 + color: Colors.white, + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox( + width: 17.rpx, + )), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 25.rpx, 83.rpx, 25.rpx, 50.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Theme( + data: ThemeData( + checkboxTheme: CheckboxThemeData( + visualDensity: + VisualDensity.compact, + materialTapTargetSize: + MaterialTapTargetSize + .shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(64), + ), + ), + unselectedWidgetColor: + Color(0xFFD3D3D3), + ), + child: Obx( + () => Checkbox( + value: loginController + .model.register_agree ?? + false, + onChanged: (newValue) async { + loginController.model + .register_agree = newValue; + loginController.updateAll(); + // 获取设备信息,需要用户点击确认隐私协议与用户协议选择框时才能获取 + // if (newValue == true) { + // Deviceconfig + // .initPlatformState(); + // } + }, + side: BorderSide( + width: 1.5, + color: + FlutterFlowTheme.of(context) + .secondaryText, + ), + activeColor: + stringToColor("#FF9F66"), + checkColor: + FlutterFlowTheme.of(context) + .info, + ), + )), + Expanded( + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0.rpx, 10.rpx, 0.rpx, 0.rpx), + child: Container( + width: bodysize.maxWidth, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: '登录页.协议1'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + fontSize: 26.rpx, + color: Colors + .white, // 可以调整为你想要的颜色 + ), + ), + TextSpan( + text: '登录页.协议2'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + fontSize: 26.rpx, + color: stringToColor( + "#FF9F66"), + ), + ), + TextSpan( + text: '登录页.协议3'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + fontSize: 26.rpx, + color: Colors + .white, // 可以调整为你想要的颜色 + ), + ), + TextSpan( + text: '登录页.协议4'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + fontSize: 26.rpx, + color: stringToColor( + "#FF9F66"), + ), + ), + TextSpan( + text: '登录页.协议5'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + fontSize: 26.rpx, + color: Colors + .white, // 可以调整为你想要的颜色 + ), + ), + TextSpan( + text: '登录页.协议6'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + fontSize: 26.rpx, + color: stringToColor( + "#FF9F66"), + ), + ), + ], + ), + ), + ), + ), + ), + ].divide(SizedBox(width: 18.rpx)), + ), + ), + ), + ], + ), + ), + ), + Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.136, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + decoration: BoxDecoration(), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 0, 0, 36.rpx), + child: Text( + '登录页.其他登录方式'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + //todo 颜色 + color: stringToColor("#FFFFFF"), + ), + ), + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 91.rpx, + height: 91.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/wechat.png", + width: 30.rpx, + height: 30.rpx, + ), + ), + Container( + width: 91.rpx, + height: 91.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/tel.png", + width: 30.rpx, + height: 30.rpx, + ), + ), + Container( + width: 91.rpx, + height: 91.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/google.png", + width: 30.rpx, + height: 30.rpx, + ), + ), + ].divide(SizedBox(width: 35.rpx)), + ), + ], + ), + ), + ], + ), + ), + ), + ), + )), + ); + } +} diff --git a/lib/pages/main_bottom/home_page.dart b/lib/pages/main_bottom/home_page.dart index 577d3d1..8ac38df 100644 --- a/lib/pages/main_bottom/home_page.dart +++ b/lib/pages/main_bottom/home_page.dart @@ -5,6 +5,8 @@ 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/home_page/SleepDataModuleWidget.dart'; +import 'package:vbvs_app/component/home_page/SleepDateWidget.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; @@ -53,6 +55,7 @@ class _HomePageState extends State { mainAxisSize: MainAxisSize.max, children: [ Padding( + //用户信息 padding: EdgeInsetsDirectional.fromSTEB( AppConstants().content_left_right_padding, 0, @@ -64,36 +67,60 @@ class _HomePageState extends State { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - CustomCard( - borderRadius: 20.rpx, - onTap: () async { - Get.toNamed("/loginPage"); - }, - title: '首页.登录' - .tr, // 虽然 title 传入了,但当前组件里没用它(可忽略或用于调试) - colors: [ - themeController.currentColor.sc1, - themeController.currentColor.sc2, - ], - child: Container( - width: 100.rpx, - height: 60.rpx, - alignment: Alignment.center, - padding: EdgeInsetsDirectional.fromSTEB( - 16.rpx, 0, 16.rpx, 0), - child: Text( - '首页.登录'.tr, - style: FlutterFlowTheme.of(context) - .titleSmall - .override( - fontFamily: 'Inter Tight', - color: themeController - .currentColor.sc19, - letterSpacing: 0.0, - ), + Obx(() { + return Visibility( + visible: + userInfoController.model.login == 0, + child: CustomCard( + borderRadius: 20.rpx, + onTap: () async { + Get.toNamed("/loginPage"); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: 100.rpx, + height: 60.rpx, + alignment: Alignment.center, + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 0, 16.rpx, 0), + child: Text( + '首页.登录'.tr, + style: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Inter Tight', + color: themeController + .currentColor.sc19, + letterSpacing: 0.0, + ), + ), + ), ), - ), - ), + ); + }), + Obx(() { + return Visibility( + visible: + userInfoController.model.login == 1, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "飞行的鱼", + style: TextStyle(color: Colors.white), + ), + Text( + "嘉兴 晴", + style: TextStyle(color: Colors.white), + ), + ], + ), + ); + }), SvgPicture.asset( 'assets/img/icon/add.svg', width: 39.rpx, @@ -106,6 +133,7 @@ class _HomePageState extends State { ), ), Padding( + //绑定数量 padding: EdgeInsetsDirectional.fromSTEB( 19.rpx, 34.rpx, 0, 21.rpx), child: ClickableContainer( @@ -116,42 +144,67 @@ class _HomePageState extends State { }, padding: EdgeInsetsDirectional.fromSTEB( 0.rpx, 10.rpx, 0, 10.rpx), - child: Container( - child: Row( - mainAxisSize: MainAxisSize.min, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - '首页.已关联体征监测设备'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: - AppConstants().title_text_fontSize, - letterSpacing: 0.0, - //todo 颜色 - color: themeController.currentColor.sc3, + Container( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '首页.已关联体征监测设备'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + //todo 颜色 + color: themeController + .currentColor.sc3, + ), + ), + Text( + '0', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + color: themeController + .currentColor.sc8, + ), + ), + ].divide(SizedBox( + width: 6.rpx, + )), + )), + Obx(() { + return Visibility( + visible: userInfoController + .model.deviceBindNum! > + 0, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 0.rpx, 8.rpx, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 14.rpx, + height: 14.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: Colors.white, ), - ), - Text( - '0', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: - AppConstants().title_text_fontSize, - letterSpacing: 0.0, - color: themeController.currentColor.sc8, - ), - ), - ].divide(SizedBox( - width: 6.rpx, - )), - )), + ), + ); + }), + ], + ), ), ), Container( + //未绑定布局 width: MediaQuery.sizeOf(context).width, height: MediaQuery.sizeOf(context).height * 0.277, constraints: BoxConstraints( @@ -178,8 +231,7 @@ class _HomePageState extends State { themeController.currentColor.sc1, themeController.currentColor.sc2, ], - title: - '首页.蓝牙绑定'.tr, // 可选,虽然这个 title 没用,但可以作为调试用 + child: Container( width: MediaQuery.sizeOf(context).width * 0.66, @@ -229,8 +281,7 @@ class _HomePageState extends State { themeController.currentColor.sc1, themeController.currentColor.sc2, ], // 渐变色是同一个色,也可以根据需要调整 - title: - '首页.蓝牙绑定'.tr, // 可选,虽然这个 title 没用,但可以作为调试用 + child: Container( width: MediaQuery.sizeOf(context).width * 0.66, @@ -278,6 +329,7 @@ class _HomePageState extends State { ), ), Padding( + //未绑定标语 padding: EdgeInsetsDirectional.fromSTEB(0, 26.rpx, 0, 0), child: Container( @@ -374,6 +426,213 @@ class _HomePageState extends State { ), ), ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 26.rpx, 0, 0), + child: Container( + // color: Colors.red, + width: bodySize.maxWidth, + height: bodySize.maxHeight * 0.107, + constraints: BoxConstraints( + minHeight: 240.rpx, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ClickableContainer( + backgroundColor: + themeController.currentColor.sc5, + highlightColor: + themeController.currentColor.sc3, + borderRadius: + AppConstants().normal_container_radius, + padding: + EdgeInsets.zero, // 原始Container没有padding + onTap: () { + // 点击逻辑放这里 + }, + child: Container( + width: bodySize.maxWidth * 0.445, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + SizedBox(height: 32.rpx), + Container( + width: 120.rpx, + height: 120.rpx, + child: Image.asset( + "assets/img/netlove.png", + fit: BoxFit.cover, + ), + ), + Text( + "首页.我的e护".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + ), + ), + SizedBox(height: 32.rpx), + ], + ), + ), + ), + ClickableContainer( + backgroundColor: + themeController.currentColor.sc5, + highlightColor: + themeController.currentColor.sc3, + borderRadius: + AppConstants().normal_container_radius, + padding: EdgeInsets + .zero, // 原本的Container没有 padding,这里设置为 zero + onTap: () { + // TODO: 替换为你需要的点击事件逻辑 + print("云关爱 被点击"); + }, + child: SizedBox( + width: bodySize.maxWidth * 0.445, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Container( + width: 120.rpx, + height: 120.rpx, + child: Image.asset( + "assets/img/mye.png", + fit: BoxFit.cover, + ), + ), + Text( + "首页.云关爱".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + ), + ), + ] + .addToStart(SizedBox(height: 32.rpx)) + .addToEnd(SizedBox(height: 32.rpx)), + ), + ), + ), + ], + ), + ), + ), + // Generated code for this Container Widget... + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 25.rpx, 0, 25.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: BorderRadius.circular( + AppConstants().normal_container_radius), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: double.infinity, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + '次卧/1201/李小北', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '首页.报告详情'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Padding( + padding: EdgeInsetsDirectional + .fromSTEB(0, 6.rpx, 0, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 14.rpx, + height: 14 + .rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: Colors.white, + ), + ), + ].divide(SizedBox(width: 22.rpx)), + ), + ], + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + SleepDateWidget(), + SleepDateWidget(), + SleepDateWidget(), + SleepDateWidget(), + SleepDateWidget(), + SleepDateWidget(), + ].divide(SizedBox( + width: 20.rpx, + )), + ), + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + SleepDataModuleWidget(), + SleepDataModuleWidget(), + SleepDataModuleWidget(), + SleepDataModuleWidget(), + SleepDataModuleWidget(), + ].divide(SizedBox( + width: 14.rpx)), // ✅ 这里加了 .rpx + ), + ), + ), + ], + ), + ), + ), + ) ], ), ), diff --git a/lib/pages/main_bottom/main_page_bottom_change.dart b/lib/pages/main_bottom/main_page_bottom_change.dart index e1dc519..de47fd5 100644 --- a/lib/pages/main_bottom/main_page_bottom_change.dart +++ b/lib/pages/main_bottom/main_page_bottom_change.dart @@ -90,47 +90,56 @@ class MainPageBottomChange extends GetView { floatingActionButton: Container(), ); } else { - return Scaffold( - backgroundColor: Colors.transparent, - body: arr[controller.model.currentIndex], - floatingActionButtonAnimator: - FloatingActionButtonAnimator.noAnimation, - floatingActionButtonLocation: - FloatingActionButtonLocation.centerDocked, - bottomNavigationBar: Theme( - data: ThemeData( - splashFactory: NoSplash.splashFactory, - highlightColor: Colors.transparent), - child: BottomNavigationBar( - unselectedItemColor: themeController.currentColor.sc4, - selectedItemColor: themeController.currentColor.sc1, - backgroundColor: themeController.currentColor.sc5, - selectedFontSize: 26.rpx, - unselectedFontSize: 26.rpx, - type: BottomNavigationBarType.fixed, - currentIndex: controller.model.currentIndex, - onTap: (index) { - Future.delayed(const Duration(milliseconds: 500), () { - if (controller.model.currentIndex != 1) { - globalController.model.hideBottomNavigationBar = false; - globalController.updateAll(); - } - }); - controller.model.currentIndex = index; - controller.updateAll(); - }, - items: [ - getBottomNavigationBarItem("assets/img/menu/home.svg", - "assets/img/menu/n_home.svg", "菜单.首页".tr), - // getBottomNavigationBarItem("assets/img/menu/report.svg", - // "assets/img/menu/n_report.svg", "菜单.报告".tr), - getBottomNavigationBarItem("assets/img/menu/e.svg", - "assets/img/menu/n_e.svg", "菜单.小e".tr), - getBottomNavigationBarItem("assets/img/menu/message.svg", - "assets/img/menu/n_message.svg", "菜单.消息".tr), - getBottomNavigationBarItem("assets/img/menu/mine.svg", - "assets/img/menu/n_mine.svg", "菜单.我的".tr), - ], + return Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgImage.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + body: arr[controller.model.currentIndex], + floatingActionButtonAnimator: + FloatingActionButtonAnimator.noAnimation, + floatingActionButtonLocation: + FloatingActionButtonLocation.centerDocked, + bottomNavigationBar: Theme( + data: ThemeData( + splashFactory: NoSplash.splashFactory, + highlightColor: Colors.transparent), + child: BottomNavigationBar( + unselectedItemColor: themeController.currentColor.sc4, + selectedItemColor: themeController.currentColor.sc1, + backgroundColor: themeController.currentColor.sc5, + selectedFontSize: 26.rpx, + unselectedFontSize: 26.rpx, + type: BottomNavigationBarType.fixed, + currentIndex: controller.model.currentIndex, + onTap: (index) { + Future.delayed(const Duration(milliseconds: 500), () { + if (controller.model.currentIndex != 1) { + globalController.model.hideBottomNavigationBar = + false; + globalController.updateAll(); + } + }); + controller.model.currentIndex = index; + controller.updateAll(); + }, + items: [ + getBottomNavigationBarItem("assets/img/menu/home.svg", + "assets/img/menu/n_home.svg", "菜单.首页".tr), + // getBottomNavigationBarItem("assets/img/menu/report.svg", + // "assets/img/menu/n_report.svg", "菜单.报告".tr), + getBottomNavigationBarItem("assets/img/menu/e.svg", + "assets/img/menu/n_e.svg", "菜单.小e".tr), + getBottomNavigationBarItem("assets/img/menu/message.svg", + "assets/img/menu/n_message.svg", "菜单.消息".tr), + getBottomNavigationBarItem("assets/img/menu/mine.svg", + "assets/img/menu/n_mine.svg", "菜单.我的".tr), + ], + ), ), ), ); diff --git a/lib/pages/person/person_page.dart b/lib/pages/person/person_page.dart new file mode 100644 index 0000000..e29fa35 --- /dev/null +++ b/lib/pages/person/person_page.dart @@ -0,0 +1,619 @@ +import 'package:ef/ef.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/tool/CustomCard.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/person/person_controller.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/pages/person/select_time.dart'; + +class PersonPage extends StatefulWidget { + const PersonPage({super.key}); + + @override + State createState() => _EPageState(); +} + +class _EPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + PersonController personController = Get.find(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: stringToColor("#242835"), + // backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: Colors.white), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + // color: Colors.grey, + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '人员资料.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtom, + ), + Positioned( + right: 20.rpx, + child: CustomCard( + borderRadius: 20.rpx, + onTap: () async { + Get.offAllNamed("/bindDeviceSuccess"); + }, + + colors: [ + stringToColor("#45D989"), + stringToColor("#00C1AA"), + ], + child: Container( + width: 100.rpx, + height: 60.rpx, + alignment: Alignment.center, + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 0, 16.rpx, 0), + child: Text( + '人员资料.保存'.tr, + style: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Inter Tight', + color: Colors.white, + letterSpacing: 0.0, + ), + ), + ), + ), + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 141.rpx, 70.rpx, 0), + child: Container( + width: double.infinity, + height: 100.rpx, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.rpx), + border: Border.all( + color: Color(0xFFF3EDED), + ), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: TextFormField( + // controller: _model.textController1, + // focusNode: _model.textFieldFocusNode1, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + fillColor: Colors.transparent, + isDense: true, + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + color: Colors.white), + hintText: '人员资料.名字输入提示'.tr, + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + filled: true, + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + color: Colors.white), + textAlign: TextAlign.center, + cursorColor: + FlutterFlowTheme.of(context).primaryText, + // validator: _model.textController1Validator + // .asValidator(context), + ), + ), + ), + ), + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 90.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 90.rpx, + height: 90.rpx, + decoration: BoxDecoration(), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/man.png", + fit: BoxFit.cover, + ), + ), + ), + Text( + '男', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Colors.white, + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(height: 14.rpx)), + ), + Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 90.rpx, + height: 90.rpx, + decoration: BoxDecoration(), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/woman.png", + fit: BoxFit.cover, + ), + ), + ), + Text( + '女', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Colors.white, + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(height: 14.rpx)), + ), + ].divide(SizedBox(width: 170.rpx)), + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 50.rpx, 70.rpx, 0), + // child: Container( + // width: double.infinity, + // height: 100.rpx, + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(50.rpx), + // border: Border.all( + // color: Color(0xFFF3EDED), + // ), + // ), + // child: Align( + // alignment: AlignmentDirectional(0, 0), + // child: Obx( + // () => Container( + // width: double.infinity, + // height: + // MediaQuery.sizeOf(context).height * 0.064, + // decoration: BoxDecoration(), + // child: InkWell( + // onTap: () { + // // 触摸收起键盘 + // FocusScope.of(context) + // .requestFocus(FocusNode()); + // Future.delayed( + // const Duration(milliseconds: 250), () { + // // 延迟执行的代码 + // showDateSelectionDialog(context, + // checkDate: + // personController.model.birthday ?? + // DateTime.now(), + // checkChange: (DateTime d) { + // personController.model.birthday = d; + // personController.updateAll(); + // print("$d"); + // }).then((d) { + // // Timer(Duration.zero, () { + // // FocusScope.of(context).unfocus(); + // // }); + // }); + // }); + // }, + // child: Container( + // constraints: + // BoxConstraints(minWidth: 200.rpx), + // child: Text( + // personController.model.birthday != null + // ? DateFormat("yyyy年MM月dd日").format( + // personController.model.birthday!) + // : '人员资料.生日输入提示'.tr, + // textAlign: TextAlign.right, + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Readex Pro', + // color: Colors.white, + // fontSize: 30.rpx, + // letterSpacing: 0, + // ), + // ), + // ), + // ), + // ), + // ), + // ), + // ), + + child: Container( + height: 100.rpx, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.rpx), + border: Border.all(color: Color(0xFFF3EDED)), + ), + child: InkWell( + onTap: () { + FocusScope.of(context).requestFocus(FocusNode()); + Future.delayed(Duration(milliseconds: 250), () { + showDateSelectionDialog( + context, + checkDate: personController.model.birthday ?? + DateTime.now(), + checkChange: (DateTime d) { + personController.model.birthday = d; + personController.updateAll(); + }, + ); + }); + }, + child: Center( + child: Text( + personController.model.birthday != null + ? DateFormat("yyyy年MM月dd日").format( + personController.model.birthday!) + : '人员资料.生日输入提示'.tr, + textAlign: TextAlign.right, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: personController.model.birthday != + null + ? Colors.white + : Colors.grey, + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0, + ), + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 18.rpx, 70.rpx, 0), + child: Container( + width: double.infinity, + height: 100.rpx, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.rpx), + border: Border.all( + color: Color(0xFFF3EDED), + ), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: TextFormField( + // controller: _model.textController3, + // focusNode: _model.textFieldFocusNode3, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + fillColor: Colors.transparent, + isDense: true, + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + ), + hintText: '人员资料.体重输入提示'.tr, + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + color: personController.model.birthday != + null + ? Colors.white + : Colors.grey, + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + filled: true, + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + color: Colors.white, + ), + textAlign: TextAlign.center, + cursorColor: + FlutterFlowTheme.of(context).primaryText, + // validator: _model.textController3Validator + // .asValidator(context), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 117.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Text( + '人员资料.疾病标题'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Color(0xFFF3F4F5), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 70.rpx, 70.rpx, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 152.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.rpx), + border: Border.all( + color: Color(0xFFE9E3E3), + ), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 8.rpx, 0, 0), + child: Icon( + Icons.arrow_back, + color: Color(0xFFE4EBF0), + size: 24.rpx, + ), + ), + Expanded( + child: Text( + '人员资料.提示'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Color(0xFFEEF3F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + ].divide(SizedBox(width: 23.rpx)), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required String type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == '1') { + Get.toNamed("/blueteethDevice"); + } + } + }, + // colors: [Colors.white.withOpacity(0.06)], // 背景色 + colors: [stringToColor("#242835")], // 背景色 + + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.asset( + imageUrl, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/person/select_time.dart b/lib/pages/person/select_time.dart new file mode 100644 index 0000000..073374f --- /dev/null +++ b/lib/pages/person/select_time.dart @@ -0,0 +1,290 @@ +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/controller/theme_controller/ThemeController.dart'; + +Future showDateSelectionDialog(BuildContext context, + {required DateTime checkDate, Function? checkChange, String title = "生日"}) { + Color checkColor = stringToColor("#D3B684"); + List years = [], months = [], days = []; + var days_select = [].obs; + int day_len = 31; + int year = DateTime.now().year; + for (var i = 0; i < 100; i++) { + years.insert(0, year - i); + } + for (var i = 1; i < 13; i++) { + months.add(i); + } + for (var i = 1; i < 32; i++) { + days.add(i); + } + int yearIndex = years.lastIndexOf(checkDate.year); + int monthIndex = months.lastIndexOf(checkDate.month); + day_len = DateTime.fromMillisecondsSinceEpoch( + DateTime(years[yearIndex], months[monthIndex] + 1) + .millisecondsSinceEpoch - + 1000) + .day; + days_select.value = days.sublist(0, day_len); + int dayIndex = days.lastIndexOf(checkDate.day); + ThemeController themeController = Get.find(); + return showDialog( + // barrierColor: stringToColor("#000320"), + 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("#242835"), + // backgroundColor: Colors.transparent, + insetPadding: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(0), + ), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular( + AppConstants().normal_container_radius), + topRight: Radius.circular( + AppConstants().normal_container_radius), + bottomLeft: Radius.circular(0.rpx), + bottomRight: Radius.circular(0.rpx), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + color: themeController.currentColor.sc5, + alignment: Alignment.centerLeft, + height: 80.rpx, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + InkWell( + child: Text( + "日期.取消".tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: AppConstants() + .normal_text_fontSize), + ), + onTap: () { + Get.back(); + }, + ), + Text( + "$title", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: + AppConstants().title_text_fontSize), + ), + // closeIconWhite, + InkWell( + child: Text( + "日期.确定".tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: AppConstants() + .normal_text_fontSize), + ), + onTap: () { + checkChange?.call(DateTime(years[yearIndex], + months[monthIndex], days[dayIndex])); + Get.back(); + }, + ) + ], + ), + ), + Container( + height: 240.rpx, + margin: EdgeInsets.only(top: 60.rpx, bottom: 60.rpx), + padding: EdgeInsets.only(left: 30.rpx, right: 30.rpx), + child: Container( + child: Row( + children: [ + Expanded( + child: Container( + padding: EdgeInsets.only( + left: 40.rpx, right: 30.rpx), + child: getOnePicker(context, years, yearIndex, + (d) { + yearIndex = d; + dayIndex = 0; + day_len = + DateTime.fromMillisecondsSinceEpoch( + DateTime( + years[yearIndex], + months[monthIndex] + + 1) + .millisecondsSinceEpoch - + 1000) + .day; + days_select.value = + days.sublist(0, day_len); + }), + ), + ), + Container( + child: Text( + "年", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + Expanded( + child: Container( + padding: EdgeInsets.only( + left: 30.rpx, right: 30.rpx), + child: getOnePicker( + context, months, monthIndex, (d) { + monthIndex = d; + dayIndex = 0; + day_len = + DateTime.fromMillisecondsSinceEpoch( + DateTime( + years[yearIndex], + months[monthIndex] + + 1) + .millisecondsSinceEpoch - + 1000) + .day; + days_select.value = + days.sublist(0, day_len); + }), + ), + ), + Container( + child: Text( + "月", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + Expanded( + child: Container( + padding: EdgeInsets.only( + left: 30.rpx, right: 40.rpx), + child: Obx( + () { + // print("${dayIndex} ${day_len}"); + return getOnePicker( + context, + days_select, + dayIndex, + (d) { + dayIndex = d; + }, + ); + }, + ), + ), + ), + Container( + child: Text( + "日", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ), + ], + ); + }, + ); +} + +getOnePicker(context, List arr, int checkIndex, Function onSelectedItemChanged, + {bool looping = false}) { + return CupertinoPicker( + key: UniqueKey(), + useMagnifier: false, + itemExtent: 80.rpx, + magnification: 1, + diameterRatio: 3, + squeeze: 1, + looping: looping, + scrollController: FixedExtentScrollController(initialItem: checkIndex), + selectionOverlay: Container(), + onSelectedItemChanged: (int value) { + // print("$value"); + onSelectedItemChanged.call(value); + }, + children: [ + ...List.generate(arr.length, (index) { + 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]}", + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + fontSize: 30.rpx)), + ); + }) + ], + ); +} diff --git a/lib/routers/routers.dart b/lib/routers/routers.dart index 55b7209..30b63f8 100644 --- a/lib/routers/routers.dart +++ b/lib/routers/routers.dart @@ -2,7 +2,9 @@ import 'package:flutter/cupertino.dart'; import 'package:vbvs_app/pages/device_bind/bind_device_success.dart'; import 'package:vbvs_app/pages/device_bind/blueteeth_device_page.dart'; import 'package:vbvs_app/pages/device_bind/device_type.dart'; +import 'package:vbvs_app/pages/device_bind/wifi_page.dart'; import 'package:vbvs_app/pages/login/login.dart'; +import 'package:vbvs_app/pages/login/other_login.dart'; import 'package:vbvs_app/pages/main_bottom/e_page.dart'; import 'package:vbvs_app/pages/main_bottom/home_page.dart'; import 'package:vbvs_app/pages/main_bottom/main_page_bottom_change.dart'; @@ -26,6 +28,9 @@ var routes = { "/blueteethDevice": (contxt) => BlueteethDevicePage(), "/personPage": (contxt) => PersonPage(), "/bindDeviceSuccess": (contxt) => BindDeviceSuccess(), + // "/wifiPage": (contxt, {arguments}) => WifiPage(connectedDeviceProp: arguments), + "/wifiPage": (contxt, {arguments}) => WifiPage(bluetoothDevice: arguments), + "/otherLoginPage": (contxt) => OtherLoginPage(), }; //2、配置onGenerateRoute 固定写法 这个方法也相当于一个中间件,这里可以做权限判断 diff --git a/pubspec.yaml b/pubspec.yaml index 0d85cb2..5e7383d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: lottie: ^3.2.0 flutter_blue_plus: ^1.35.3 permission_handler: ^12.0.0+1 + # geolocator: ^13.0.4 dev_dependencies: