更新棉花糖

This commit is contained in:
wyf
2025-06-16 09:32:24 +08:00
parent d8b46c41ad
commit acde8340a8
27 changed files with 6762 additions and 658 deletions

View File

@@ -0,0 +1,616 @@
import 'dart:async';
import 'package:EasyDartModule/EasyDartModule.dart' as edm;
import 'package:EasyDartModule/base/logger/Logger.dart';
import 'package:easydevice/easydevice.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/color/ServiceConstant.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/DailyLogUtils.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/component/tool/cmd.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/model/BleDeviceData.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
class DeviceComponentWidget extends StatefulWidget {
BlueToothDataModel bleDevice;
DeviceComponentWidget({
super.key,
required this.bleDevice,
});
@override
State<DeviceComponentWidget> createState() => _DeviceComponentWidgetState();
}
class _DeviceComponentWidgetState extends State<DeviceComponentWidget> {
ThemeController themeController = Get.find();
MHTBlueToothController blueteethBindController = Get.find();
var lisObj;
@override
Widget build(BuildContext context) {
Map device = {
"name": widget.bleDevice.name,
"mac": widget.bleDevice.mac,
"rssi": widget.bleDevice.scanResult.rssi,
"bind": widget.bleDevice.bind,
};
return ClickableContainer(
backgroundColor: Colors.white,
highlightColor: Colors.white,
borderRadius: 20.rpx,
padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 36.rpx, 55.rpx, 52.rpx),
onTap: () async {},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Row(
children: [
Text(
"${device['name']}",
style: TextStyle(
color: stringToColor("#333333"), fontSize: 30.rpx),
),
SizedBox(
width: 10.rpx,
),
Obx(() {
if (blueteethBindController.currentDeviceMac.value ==
device['mac']) {
return SizedBox(
width: 24.rpx,
height: 24.rpx,
child: CircularProgressIndicator(
strokeWidth: 1,
valueColor: AlwaysStoppedAnimation<Color>(
stringToColor("#929699")),
),
);
}
return Container();
}),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(minWidth: 106.rpx),
child: Text(
"MAC".tr,
style: TextStyle(
color: stringToColor("#929699"),
fontSize: 26.rpx,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${device['mac']}",
style: TextStyle(
fontSize: 26.rpx,
color: stringToColor("#333333")),
),
].divide(SizedBox(width: 33.rpx)),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(minWidth: 106.rpx),
child: Text(
"信号强度".tr,
style: TextStyle(
color: stringToColor("#929699"),
fontSize: 26.rpx,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${device['rssi']}" + "dBm".tr,
style: TextStyle(
fontSize: 26.rpx,
color: stringToColor("#333333")),
),
].divide(SizedBox(width: 33.rpx)),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(minWidth: 106.rpx),
child: Text(
"设备状态".tr,
style: TextStyle(
color: stringToColor("#929699"),
fontSize: 26.rpx,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
"${device['bind'] == true ? "已被绑定" : "可绑定"}",
style: TextStyle(
fontSize: 26.rpx,
color: device['bind'] == true
? stringToColor("#FF7159")
: stringToColor("#6BFDAC"),
),
),
].divide(SizedBox(width: 33.rpx)),
),
].divide(SizedBox(height: 37.rpx)),
),
),
CustomCard(
borderRadius: 16.rpx,
onTap: () async {
try {
// 连接前暂停扫描
blueteethBindController.pauseScanning();
if (blueteethBindController.currentDeviceMac?.value !=
null &&
blueteethBindController
.currentDeviceMac!.value.isNotEmpty) {
if (blueteethBindController.currentDeviceMac?.value !=
device['mac']) {
showConfirmDialog(
context, Container(), "其他设备正在绑定中,是否终止其他设备绑定?".tr,
onConfirm: () {
blueteethBindController.currentDeviceMac.value = "";
blueteethBindController.updateAll();
}, onCancel: () {
blueteethBindController.resumeScanning();
});
}
}
blueteethBindController.currentDeviceMac?.value =
device['mac']!;
blueteethBindController.updateAll();
if (device['bind'] == true) {
await showHaveBindDialog(context);
blueteethBindController.currentDeviceMac.value = "";
blueteethBindController.resumeScanning();
} else {
showConfirmDialog(
context,
Container(),
'是否确认绑定?'.tr,
onConfirm: () async {
//连接蓝牙 ,获取设备信息
blueteethBindController.currentDeviceMac.value =
widget.bleDevice.mac;
blueteethBindController.updateAll();
String mac =
await getBindTHMAC(context, widget.bleDevice);
if (mac != null && mac.isNotEmpty) {
bool flag =
await fillTHMac(mac, widget.bleDevice, context);
if (!flag) {
return;
}
}
blueteethBindController.currentFullDevice =
widget.bleDevice;
ApiResponse response = await blueteethBindController
.bindDeviceAndMAC(widget.bleDevice, context);
TopSlideNotification.show(context,
text: response.msg!);
if (response.code == HttpStatusCodes.ok) {
//更新设备绑定流程
Get.toNamed("/mHTwifiPage",arguments: widget.bleDevice);
THapp bledevice = THapp(
device: widget.bleDevice.scanResult.device);
blueteethBindController.currentDevice = bledevice;
blueteethBindController.currentDeviceMac.value = "";
} else {
blueteethBindController.resumeScanning();
blueteethBindController.currentDeviceMac.value = "";
blueteethBindController.updateAll();
TopSlideNotification.show(
context,
text: response.msg ?? "蓝牙绑定.连接异常".tr,
textColor: themeController.currentColor.sc9,
);
}
},
onCancel: () {
print('用户点击了取消');
blueteethBindController.currentDeviceMac.value = "";
blueteethBindController.resumeScanning();
blueteethBindController.updateAll();
},
);
}
} catch (e) {
Navigator.pop(context);
TopSlideNotification.show(
context,
text: "连接异常".tr,
textColor: themeController.currentColor.sc9,
);
edm.EasyDartModule.logger.info("连接异常: $e");
DailyLogUtils.writeLog("连接异常: $e");
} finally {
// 确保在任何情况下都恢复扫描
if (blueteethBindController
.currentDeviceMac.value.isEmpty) {
blueteethBindController.resumeScanning();
}
}
},
colors: [stringToColor("1592AA"), stringToColor("006FA3")],
child: Container(
width: 150.rpx,
height: 90.rpx,
alignment: Alignment.center,
child: Text(
"添加".tr,
style: TextStyle(color: Colors.white, fontSize: 26.rpx),
),
),
),
],
),
].divide(SizedBox(height: 37.rpx)),
),
);
}
//更新设备绑定状态
void updateDeviceBindStatus(BleDeviceData device) {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_setting;
String mac = device.mac!;
String type = "device_bind_status_${mac}";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
Map<String, dynamic> data = {
"type": type,
"mac": mac,
"wifi": false,
"celibration": false,
"person_info": false,
"time": DateTime.now().millisecondsSinceEpoch,
};
requestWithLog(
logTitle: "更新用户绑定流程",
method: MyHttpMethod.put,
queryUrl: queryUrl,
data: data,
onSuccess: (res) {},
onFailure: (res) {},
);
}
Widget _buildDeviceInfoSection(device, BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0.rpx, 30.rpx, 0.rpx),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
device.name ?? '蓝牙绑定.默认设备名称'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFF6FAFD),
fontSize: 30.rpx,
letterSpacing: 0.0,
),
),
Obx(() {
if (blueteethBindController.currentDeviceMac.value ==
device.mac) {
return SizedBox(
width: 24.rpx,
height: 24.rpx,
child: CircularProgressIndicator(
strokeWidth: 1,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
);
}
return Container();
}),
],
),
),
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.bind == false ? '蓝牙绑定.可绑定'.tr : '蓝牙绑定.已被绑定'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: device.bind == false
? const Color(0xFF1AD2B5)
: themeController.currentColor.sc9,
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
],
),
],
),
Text(
"版本".tr + '${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,
)),
);
}
//获取传感器mac
Future<String> getBindTHMAC(
BuildContext context, BlueToothDataModel device) async {
const int maxRetries = 2;
const Duration timeout = Duration(seconds: 5);
String? macAddress;
try {
// 连接设备
THapp bledevice = THapp(device: device.scanResult.device);
await bledevice.connect();
var res2 = bledevice.isConnected;
if (!res2) {
edm.EasyDartModule.logger.error("蓝牙连接失败");
DailyLogUtils.printLog("蓝牙连接失败");
TopSlideNotification.show(
context,
text: "蓝牙连接失败".tr,
textColor: themeController.currentColor.sc9,
);
throw Exception("蓝牙连接失败");
}
blueteethBindController.blueConnectFlag.value = 2;
blueteethBindController.currentDevice = bledevice;
var read = bledevice.getresource('fff0/fff1');
await read!.characteristic.setNotifyValue(true);
var write = bledevice.getresource('fff0/fff2');
for (int attempt = 0; attempt < maxRetries; attempt++) {
var completer = Completer<String>();
StreamSubscription? subscription;
subscription = read.characteristic.onValueReceived.listen((onData) {
if (onData.length >= 14) {
// 按照你提供的协议,返回数据长度至少 17字节这里保险起见 14起步
String parsedMac = parseMacFromBleResponse(onData);
completer.complete(parsedMac);
}
});
// 发送查询命令
var order = [
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x03,
0x40,
0x01,
0x01,
0x00,
0x45,
0xFD
];
await write!.characteristic.write(order);
try {
macAddress = await completer.future.timeout(timeout);
await subscription.cancel();
break; // 成功拿到,跳出重试
} catch (e) {
await subscription.cancel();
if (attempt == maxRetries - 1) {
TopSlideNotification.show(
context,
text: "获取MAC地址超时请重试".tr,
textColor: themeController.currentColor.sc9,
);
throw Exception("获取MAC地址失败");
}
}
}
if (macAddress == null) {
throw Exception("未能获取到MAC地址");
}
// device.macA = macAddress;
print('MAC地址: $macAddress');
return macAddress;
} catch (e) {
blueteethBindController.currentDeviceMac.value = "";
edm.EasyDartModule.logger.error("蓝牙获取MAC失败$e");
DailyLogUtils.printLog("蓝牙获取MAC失败$e");
TopSlideNotification.show(
context,
text: "设备连接失败,请重试".tr,
textColor: themeController.currentColor.sc9,
);
rethrow;
}
}
fillTHMac(
String mac, BlueToothDataModel bleDevice, BuildContext context) async {
bool flag = false;
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.get_bluetooth_device_status;
String queryUrl = "$serviceAddress$serviceName$serviceApi";
String macParam =
"mac=${Uri.encodeQueryComponent(mac.replaceAll(':', ''))}";
if (queryUrl.contains('?')) {
queryUrl += '&$macParam';
} else {
queryUrl += '?$macParam';
}
await requestWithLog(
logTitle: "获取设备状态",
method: MyHttpMethod.get,
queryUrl: queryUrl,
onSuccess: (res) {
flag = true;
if (res.code != HttpStatusCodes.ok) return;
if (res.data != null && res.data is List) {
List<dynamic> responseList = res.data;
// 查找当前MAC对应的数据
String macKey = mac.replaceAll(':', '').toUpperCase();
for (var item in responseList) {
if (item['mac'].toString().toUpperCase() == macKey) {
// 更新 bleDevice 的状态
//如果传感器已经绑定 暂时不处理
// bleDevice.bind = item['bind'] ?? bleDevice.bind;
bleDevice.macA = item['mac'];
if (item['bindMac'] != null &&
item['bindMac'].toString().isNotEmpty) {
bleDevice.macB = item['bindMac'];
}
break;
}
}
}
},
onFailure: (res) {
flag = false;
TopSlideNotification.show(context,
text: "获取设备状态失败".tr, textColor: themeController.currentColor.sc9);
},
);
return flag;
}
}
String parseMacFromBleResponse(List<int> data) {
// 先做简单的合法性判断
if (data.length >= 17 &&
data[0] == 0xFF &&
data[1] == 0xFF &&
data[2] == 0xFF &&
data[3] == 0xFF &&
data[6] == 0x40 &&
data[7] == 0x01) {
// 取出 Byte8 ~ Byte13
List<int> macBytes = data.sublist(8, 14);
String macAddress = macBytes
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
.join('');
return macAddress;
} else {
throw Exception("BLE返回数据格式不正确");
}
}

View File

@@ -0,0 +1,42 @@
import 'package:ef/ef.dart';
import 'package:json_annotation/json_annotation.dart';
part 'mht_device_calibration_controller.g.dart'; // 由json_serializable自动生成的部分
@JsonSerializable()
class MHTDeviceCalibrationModel {
MHTDeviceCalibrationModel();
factory MHTDeviceCalibrationModel.fromJson(Map<String, dynamic> json) {
try {
return _$MHTDeviceCalibrationModelFromJson(json);
} catch (e) {
return MHTDeviceCalibrationModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
}
}
// 序列化为JSON时的异常处理
Map<String, dynamic> toJson() => _$MHTDeviceCalibrationModelToJson(this);
}
class MHTDeviceCalibrationController
extends GetControllerEx<MHTDeviceCalibrationModel> {
MHTDeviceCalibrationController() {
attr = GetModel(MHTDeviceCalibrationModel()).obs;
}
RxInt process = 0.obs; //校准流程 0.离床校准 1.位置校准
RxInt bed_calibration = 0.obs; //0.未完成 1.完成
RxInt position_calibration = 0.obs; //0.未完成 1.完成
RxInt bed_type = 0.obs; //0.单人 1.双人
RxString tips = "".obs;
RxInt flag = 0.obs; //0没有开始 1校准中 2.校准完成 3.校准失败
RxString statusContext = "".obs;
RxInt cd = 10000.obs;
bool forceStart = false;
RxInt motionTips = 0.obs;//体动提示
RxInt inBedTips = 0.obs;//在床提示
}

View File

@@ -0,0 +1,15 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'mht_device_calibration_controller.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MHTDeviceCalibrationModel _$MHTDeviceCalibrationModelFromJson(
Map<String, dynamic> json) =>
MHTDeviceCalibrationModel();
Map<String, dynamic> _$MHTDeviceCalibrationModelToJson(
MHTDeviceCalibrationModel instance) =>
<String, dynamic>{};

View File

@@ -0,0 +1,264 @@
import 'dart:async';
import 'dart:convert';
import 'package:EasyDartModule/EasyDartModule.dart';
import 'package:easydevice/src/app/thapp.dart';
import 'package:ef/ef.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:vbvs_app/common/color/ServiceConstant.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/DailyLogUtils.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
import 'package:vbvs_app/pages/mh_page/user/controller/mht_register_controller.dart';
part 'mht_bluetooth_controller.g.dart';
@JsonSerializable()
class MHTBlueToothModel {
bool? bluetooth = false; //蓝牙开关
double? singal = -100;
@JsonKey(ignore: true)
List<BlueToothDataModel>? blueRawData; //蓝牙原始数据
@JsonKey(ignore: true)
List<BlueToothDataModel>? deviceDataStatus; //已经请求过状态的数据
int? read = 0;
int? deviceType; //绑定设备类型
bool wifiPassShow = false;
String? wifiPass;
MHTBlueToothModel();
static MHTBlueToothModel fromJson(Map<String, dynamic> json) =>
_$MHTBlueToothModelFromJson(json);
Map<String, dynamic> toJson() => _$MHTBlueToothModelToJson(this);
}
class MHTBlueToothController extends GetControllerEx<MHTBlueToothModel> {
MHTBlueToothController() {
attr = GetModel(MHTBlueToothModel()).obs;
}
Timer? _statusTimer;
MHTRegisterController registerController = Get.find();
RxString search = "".obs;
RxString currentDeviceMac = "".obs; //当前正在绑定的设备用来显示loading
THapp? currentDevice; //当前连接的设备
BlueToothDataModel? currentFullDevice; //当前连接的设备
RxInt blueConnectFlag = 0.obs; //当前蓝牙连接状态 0.正在连接 1.未连接 2.已连接
RxBool bluetoothStatus = false.obs; //蓝牙开启状态
RxInt connectStatus = 0.obs; //当前wifi连接状态 0未连接 1已连接
RxInt netType = 0.obs; //当前网络类型 0.正在检测 1.wifi 2.4g设备 3.未知
RxInt wifiConnectStatus = 1.obs; //获取wifi状态 0.正在检测 1.已检测完
RxMap selectWifi = {}.obs; //正在连接wifi信息
int returnPage = 0; //0返回首页 1.返回设备列表
RxInt wifiStatus = 0.obs; //wifi连接状态 0未连接 1已连接
RxList wifiList = [].obs;
RxMap connect_wifi = {}.obs;
RxString? cid = "".obs;
void startStatusPolling() {
updateDeviceStatus().then((res) {
if (res.code == HttpStatusCodes.ok) {
updateAll();
} else {
safeShowNotification(res.msg ?? "获取设备状态异常");
EasyDartModule.logger.info("获取设备状态异常: $res");
DailyLogUtils.writeLog("获取设备状态异常: $res");
}
});
if (_statusTimer == null) {
_statusTimer = Timer.periodic(Duration(seconds: 2), (timer) {
updateDeviceStatus().then((res) {
if (res.code == HttpStatusCodes.ok) {
updateAll();
} else {
safeShowNotification(res.msg ?? "获取设备状态异常");
EasyDartModule.logger.info("获取设备状态异常: $res");
DailyLogUtils.writeLog("获取设备状态异常: $res");
}
}).catchError((e, stack) {
print("updateDeviceStatus 执行异常: $e\n$stack");
safeShowNotification("设备状态请求失败");
EasyDartModule.logger.info("设备状态异常: $e");
DailyLogUtils.writeLog("设备状态异常: $e");
});
});
}
}
void stopStatusPolling() {
_statusTimer?.cancel();
_statusTimer = null;
}
var shouldScan = true.obs;
void pauseScanning() {
shouldScan.value = false;
update();
}
// 恢复扫描
void resumeScanning() {
shouldScan.value = true;
update();
}
Future<ApiResponse> updateDeviceStatus() async {
try {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.get_bluetooth_device_status;
String queryUrl = "$serviceAddress$serviceName$serviceApi";
if (model.blueRawData != null && model.blueRawData!.isNotEmpty) {
final macParams = model.blueRawData!
.map((device) =>
"mac=${Uri.encodeQueryComponent(device.mac!.replaceAll(':', ''))}")
.join("&");
if (queryUrl.contains('?')) {
queryUrl += '&$macParams';
} else {
queryUrl += '?$macParams';
}
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
var responseData =
response.data is String ? jsonDecode(response.data) : response.data;
ApiResponse res =
ApiResponse.fromJson(responseData, (object) => object);
if (res.code != HttpStatusCodes.ok) return res;
if (response.data['data'] != null && response.data['data'] is List) {
List<dynamic> responseList = response.data['data'];
// 新建一个 Map便于快速通过 mac 查找返回的设备状态
final Map<String, dynamic> responseMap = {
for (var item in responseList)
item['mac'].toString().toUpperCase(): item
};
// 遍历 blueRawData更新 bind 状态
for (var device in model.blueRawData!) {
final macKey = device.mac!.replaceAll(':', '').toUpperCase();
if (responseMap.containsKey(macKey)) {
var item = responseMap[macKey];
// 更新 device 绑定状态等信息
device.bind = item['bind'] ?? device.bind;
device.mac = item['bindMac'] ?? device.mac;
}
}
model.deviceDataStatus =
List<BlueToothDataModel>.from(model.blueRawData!);
} else {
model.deviceDataStatus = [];
}
updateAll();
return res;
} else {
model.deviceDataStatus = [];
}
} catch (e) {
print("获取设备状态异常: $e");
EasyDartModule.logger.info("获取设备状态异常: $e");
DailyLogUtils.writeLog("获取设备状态异常: $e");
return ApiResponse(code: -1, msg: "请求失败".tr);
}
return ApiResponse(code: -1, msg: "未知错误".tr);
}
bindDeviceAndMAC(BlueToothDataModel bleDevice, BuildContext context) async {
try {
if ((bleDevice.macA == null || bleDevice.macA.isEmpty) &&
(bleDevice.macB == null || bleDevice.macB.isEmpty)) {
TopSlideNotification.show(context,
text: "传感器mac读取失败".tr, textColor: themeController.currentColor.sc9);
currentDeviceMac.value = "";
return;
}
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.device_bind;
String queryUrl = "$serviceAddress$serviceName$serviceApi";
var data = {
"deviceType": model.deviceType,
"mac": bleDevice.mac,
"macA": bleDevice.macA,
if (bleDevice.macB != null && bleDevice.macB!.isNotEmpty)
"macB": bleDevice.macB,
};
var response =
await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data));
if (response != null) {
var responseData =
response.data is String ? jsonDecode(response.data) : response.data;
ApiResponse res =
ApiResponse.fromJson(responseData, (object) => object);
MyUtils.formatResponse(res, "绑定成功".tr, "绑定成功".tr);
if (res.code == HttpStatusCodes.ok) {
// PersonController personController = Get.find();
// personController.currentPersonId.value = res.data['id'];
currentDeviceMac.value = "";
return res;
} else {
return res;
}
} else {
return ApiResponse(code: -1, msg: "服务器失败".tr);
}
} catch (e) {
EasyDartModule.logger.info("绑定异常: $e");
DailyLogUtils.writeLog("蓝牙绑定: $e");
}
return ApiResponse(code: -1, msg: "未知错误".tr);
}
}
void safeShowNotification(String msg) {
try {
final ctx = Get.context;
if (ctx != null && ctx.mounted) {
TopSlideNotification.show(
ctx,
text: msg,
textColor: themeController.currentColor.sc9,
);
} else {
print("TopSlideNotification 未显示context 不可用或未挂载");
}
} catch (e, stack) {
// print("TopSlideNotification 显示异常: $e\n$stack");
}
}

View File

@@ -0,0 +1,26 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'mht_bluetooth_controller.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MHTBlueToothModel _$MHTBlueToothModelFromJson(Map<String, dynamic> json) =>
MHTBlueToothModel()
..bluetooth = json['bluetooth'] as bool?
..singal = (json['singal'] as num?)?.toDouble()
..read = (json['read'] as num?)?.toInt()
..deviceType = (json['deviceType'] as num?)?.toInt()
..wifiPassShow = json['wifiPassShow'] as bool
..wifiPass = json['wifiPass'] as String?;
Map<String, dynamic> _$MHTBlueToothModelToJson(MHTBlueToothModel instance) =>
<String, dynamic>{
'bluetooth': instance.bluetooth,
'singal': instance.singal,
'read': instance.read,
'deviceType': instance.deviceType,
'wifiPassShow': instance.wifiPassShow,
'wifiPass': instance.wifiPass,
};

View File

@@ -0,0 +1,229 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/color/app_uri_status.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/TopSlideNotification.dart';
import 'package:vbvs_app/controller/device/device_type_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';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
class MHTBindDeviceTypePage extends StatefulWidget {
int type = 0; //0绑定设备 1.查询我的设备
MHTBindDeviceTypePage({super.key, this.type = 0});
@override
State<MHTBindDeviceTypePage> createState() => _MHTBindDeviceTypePageState();
}
class _MHTBindDeviceTypePageState extends State<MHTBindDeviceTypePage> {
GlobalController globalController = Get.find();
UserInfoController userInfoController = Get.find();
// BlueteethBindController blueteethBindController = Get.find();
MHTBlueToothController blueteethBindController = Get.find();
ThemeController themeController = Get.find();
DeviceTypeController deviceTypeController = Get.find();
@override
void initState() {
super.initState();
deviceTypeController.getDeviceType().then((response) {
if (response.code != HttpStatusCodes.ok) {
// 延迟到 widget 构建后再执行 show
TopSlideNotification.show(
context, // 用当前页面 context 替代 Get.context!
text: response.msg ?? "服务器.失败".tr,
textColor: themeController.currentColor.sc9,
);
}
});
}
@override
Widget build(BuildContext context) {
int read = blueteethBindController.model.read!;
if (blueteethBindController.model.read == 1) {
//需要弹窗显示教程
}
return LayoutBuilder(
builder: (context, bodySize) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/new_background.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent, // 加上这一行
appBar: AppBar(
backgroundColor: Colors.transparent,
// backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
titleSpacing: 0,
// leading: returnIconButtom,
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: themeController.currentColor.sc3,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 使用 Obx 来监听 deviceTypeList 的变化
SizedBox(height: 26.rpx), // 开始的间隔
Text(
"选择类型",
style: TextStyle(color: Colors.white, fontSize: 30.rpx),
),
SizedBox(height: 32.rpx), // 开始的间隔
Obx(() {
return Column(
children: [
...deviceTypeController.deviceTypeList.value
.map((device) {
return Padding(
padding: EdgeInsets.only(
bottom: 26.rpx), // 添加每个设备之间的间隔
child: _buildDeviceCard(
context,
title: device['name'], // 这里假设 device 是一个 Map
imageUrl: device['image'],
type: device['type'],
desc: device['desc'] ?? [],
reg: device['device_reg'] ?? [],
),
);
}).toList(),
SizedBox(height: 26.rpx), // 结束的间隔
],
);
}),
],
),
),
),
),
),
),
),
);
}
Widget _buildDeviceCard(BuildContext context,
{required String title,
required String imageUrl,
required double type,
required List<dynamic>? desc,
required List<dynamic>? reg}) {
if (type == 1) {
return SizedBox.shrink();
}
Map data = {
"reg":reg,
"type":type,
};
return CustomCard(
borderRadius: 20.rpx,
onTap: () {
MHTBlueToothController mhtBlueToothController = Get.find();
mhtBlueToothController.model.deviceType = type.toInt();
Get.toNamed("/mHTBlueteethDevicePage", arguments: data);
},
colors: [Colors.white],
child: Container(
width: double.infinity,
height: MediaQuery.sizeOf(context).height * 0.135,
constraints: BoxConstraints(minHeight: 220.rpx),
padding: EdgeInsets.symmetric(horizontal: 30.rpx),
child: Row(
children: [
SizedBox(width: 58.rpx),
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Image.network(
imageUrl,
width: 177.rpx,
height: 85.rpx,
fit: BoxFit.cover,
),
),
SizedBox(width: 30.rpx),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
title,
style: TextStyle(
fontFamily: 'Inter',
color: stringToColor("#333333"),
fontSize: 30.rpx,
letterSpacing: 0.0,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (desc != null && desc.isNotEmpty) ...[
SizedBox(height: 10.rpx),
Text(
desc.join(' '),
style: TextStyle(
fontFamily: 'Inter',
color: stringToColor("#666666"),
fontSize: 24.rpx,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left, // 或 center 看你整体布局需求
),
],
],
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,717 @@
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';
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/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/pages/mh_page/component/mht_bind_dialog.dart';
import 'package:vbvs_app/pages/mh_page/device/component/DeviceComponentWidget.dart';
import 'package:vbvs_app/pages/mh_page/device/controller/mht_bluetooth_controller.dart';
import 'package:vbvs_app/pages/mh_page/device/model/BlueToothDataModel.dart';
class MHTBlueteethDevicePage extends StatefulWidget {
var data;
MHTBlueteethDevicePage({super.key, required this.data});
@override
State<MHTBlueteethDevicePage> createState() => _MHTBlueteethDevicePageState();
}
class _MHTBlueteethDevicePageState extends State<MHTBlueteethDevicePage> {
MHTBlueToothController mhtBlueToothController = Get.find();
GlobalController globalController = Get.find();
UserInfoController userInfoController = Get.find();
ThemeController themeController = Get.find();
late FlutterBluePlus flutterBlue;
List<ScanResult> 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 = ["", "", ""];
StreamSubscription<List<ScanResult>>? _scanSubscription;
@override
void initState() {
super.initState();
mhtBlueToothController.model.blueRawData = [];
mhtBlueToothController.model.deviceDataStatus = [];
flutterBlue = FlutterBluePlus();
_checkBluetoothPermission();
mhtBlueToothController.startStatusPolling();
mhtBlueToothController.search.value = "";
mhtBlueToothController.currentDeviceMac?.value = "";
}
Future<void> _checkBluetoothPermission() async {
PermissionStatus bluetoothStatus = await Permission.bluetooth.status;
PermissionStatus locationStatus = await Permission.location.status;
if (bluetoothStatus.isGranted && locationStatus.isGranted) {
_startScanning();
_startPeriodicScan();
} else {
_requestBluetoothPermission();
}
}
Future<void> _requestBluetoothPermission() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.location,
].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("权限提示".tr),
content: Text("应用需要蓝牙和位置权限才能扫描设备。请授予权限。".tr),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text("确定".tr),
),
],
);
},
);
}
void _startScanning() async {
if (!mounted || isScanning || !mhtBlueToothController.shouldScan.value) return;
_scanSubscription?.cancel();
var bluetoothState = await FlutterBluePlus.isOn;
mhtBlueToothController.model.bluetooth = bluetoothState;
mhtBlueToothController.updateAll();
if (!bluetoothState && !_isDialogShowing) {
_isDialogShowing = true;
mhtBlueToothController.model.blueRawData = [];
mhtBlueToothController.model.deviceDataStatus = [];
mhtBlueToothController.updateAll();
await _showBluetoothNotEnabledDialog();
_isDialogShowing = false;
return;
}
if (!isScanning) {
setState(() {
isScanning = true;
});
await FlutterBluePlus.startScan(timeout: Duration(seconds: 10));
_scanSubscription = FlutterBluePlus.scanResults.listen((results) {
if (!mounted) return;
final signalThreshold = mhtBlueToothController.model.singal!;
final searchKey = mhtBlueToothController.search.value.trim().toLowerCase();
final filteredResults = results.where((r) {
final localName = r.advertisementData.localName;
final isTarget = r.rssi > signalThreshold &&
isTargetDevice(localName, widget.data['reg'].cast<String>());
if (!isTarget) return false;
final name = r.advertisementData.advName.toLowerCase();
String macAddress = r.device.remoteId.str;
final mac = macAddress.replaceAll(':', '');
final search = searchKey.trim().replaceAll(':', '').toLowerCase();
if (search.isNotEmpty &&
!name.contains(search) &&
!mac.replaceAll(':', '').toLowerCase().contains(search)) {
return false;
}
return true;
}).map((r) {
return BlueToothDataModel.fromScanResult(
r, widget.data['type']?.toInt(),
bind: false,
name: r.advertisementData.localName,
mac: r.device.remoteId.str.replaceAll(':', ''));
}).toList();
setState(() {
mhtBlueToothController.model.blueRawData = filteredResults;
});
});
await Future.delayed(Duration(seconds: 10));
await FlutterBluePlus.stopScan();
if (mounted) {
setState(() {
isScanning = false;
});
}
}
}
void _startPeriodicScan() {
_timer = Timer.periodic(Duration(seconds: 10), (timer) {
if (mhtBlueToothController.shouldScan.value && !isScanning) {
_startScanning();
}
});
}
void _stopScanning() {
if (isScanning) {
FlutterBluePlus.stopScan();
_scanSubscription?.cancel();
if (mounted) {
setState(() {
isScanning = false;
});
}
}
}
void _stopPeriodicScan() {
_timer?.cancel();
}
@override
void dispose() {
_stopPeriodicScan();
_stopScanning();
_scanSubscription?.cancel();
connectTimer?.cancel();
mhtBlueToothController.stopStatusPolling();
mhtBlueToothController.model.blueRawData = [];
mhtBlueToothController.model.deviceDataStatus = [];
super.dispose();
}
bool isTargetDevice(String? name, List<String> keywords) {
if (name == null) return false;
return keywords.any((k) => name.contains(k));
}
_showBluetoothNotEnabledDialog() async {
await showTipDialog(
backgroundColor: Colors.white,
context,
Column(
children: [
Text(
"蓝牙未开启".tr,
style: TextStyle(
fontSize: AppConstants().title_text_fontSize,
color: stringToColor("#333333"),
),
),
SizedBox(
height: 20.rpx,
),
Text(
"请先打开蓝牙在进行设备扫描".tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: stringToColor("#333333"),
),
),
],
));
}
@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/images/new_background.png'),
fit: BoxFit.fill,
),
),
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
appBar: AppBar(
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
backgroundColor: Colors.transparent,
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: themeController.currentColor.sc3,
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: [
Expanded(
child: Column(
children: [
Padding(
padding:
EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
stringToColor("FCFCFC"),
stringToColor("CEECE3")
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.circular(20.rpx),
),
child: Align(
alignment: AlignmentDirectional(0, 0),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0, 30.rpx, 0, 30.rpx),
child: Obx(() {
return Text(
(mhtBlueToothController.model.bluetooth ==
null ||
mhtBlueToothController
.model.bluetooth ==
false)
? "等待扫描".tr
: '扫描中'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
color: stringToColor("#003058"),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
);
}),
),
),
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
stringToColor("FCFCFC"),
stringToColor("CEECE3")
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
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(
'最小信号强度'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
color: stringToColor("#003058"),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
Expanded(
child: Obx(() {
return Slider(
activeColor: Color(0xFF1FCC9B),
inactiveColor:
FlutterFlowTheme.of(context)
.alternate,
min: -100,
max: 50,
value:
mhtBlueToothController.model.singal!,
onChanged: (newValue) {
newValue = double.parse(
newValue.toStringAsFixed(0));
mhtBlueToothController.model.singal =
newValue;
_startScanning();
mhtBlueToothController.updateAll();
},
);
}),
),
Obx(() {
return Text(
'${mhtBlueToothController.model.singal!.toInt()}',
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
color: stringToColor("#003058"),
fontSize: 26.rpx,
letterSpacing: 0.0,
),
);
}),
].divide(SizedBox(width: 30.rpx)),
),
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(
color: themeController.currentColor.sc3,
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,
decoration: BoxDecoration(),
child: SvgPicture.asset(
'assets/img/icon/query.svg',
fit: BoxFit.cover,
color: stringToColor("#333333"),
),
),
),
Expanded(
child: Container(
width: 100.rpx,
height: 90.rpx,
decoration: BoxDecoration(
color: FlutterFlowTheme.of(context)
.secondaryBackground,
),
child: Align(
alignment:
AlignmentDirectional(-1, 0),
child: TextFormField(
initialValue:
mhtBlueToothController
.search.value,
onChanged: (Value) {
mhtBlueToothController
.search.value = Value;
},
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: false,
fillColor: themeController
.currentColor.sc22,
),
style:
FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
),
cursorColor:
stringToColor("#003058"),
),
),
),
),
].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: stringToColor("#333333"),
),
),
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc4,
borderRadius: 6.rpx,
padding: EdgeInsets.zero,
onTap: () async {
_startScanning();
},
child: Text(
'搜索'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 30.rpx,
letterSpacing: 0.0,
color: stringToColor("#333333"),
),
),
),
].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: Obx(() {
return Text(
'匹配出的外围设备'.tr +
"${mhtBlueToothController.model.deviceDataStatus!.length}",
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 30.rpx,
letterSpacing: 0.0,
color: themeController.currentColor.sc3,
),
);
}),
),
),
),
Obx(() {
if (mhtBlueToothController
.model.deviceDataStatus!.isNotEmpty) {
return Expanded(
child: Container(
width: double.infinity,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
...mhtBlueToothController
.model.deviceDataStatus!
.map((device) {
return DeviceComponentWidget(
bleDevice: device,
);
})
.toList()
.divide(SizedBox(height: 30.rpx))
.addToEnd(SizedBox(height: 30.rpx)),
],
),
),
),
);
}
return Container();
}),
].divide(SizedBox(
height: 30.rpx,
)),
)),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(0, 52.rpx, 0, 0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.rpx),
border: Border.all(
color: themeController.currentColor.sc4
.withOpacity(0.5),
width: AppConstants().border_width,
),
),
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: Container(
width: 23.rpx,
height: 23.rpx,
decoration: BoxDecoration(),
child: SvgPicture.asset(
'assets/img/icon/tips.svg',
fit: BoxFit.cover,
color: themeController.currentColor.sc4,
),
),
),
Expanded(
child: Text(
'蓝牙绑定提示'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
color: themeController.currentColor.sc4,
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
),
].divide(SizedBox(width: 23.rpx)),
),
),
),
),
].divide(SizedBox(height: 30.rpx)),
),
),
),
),
),
),
);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
class BlueToothDataModel {
String name;
bool bind;
String mac;
ScanResult scanResult;
String macA;
String macB;
int type;
BlueToothDataModel({
this.name = '',
required this.bind,
required this.mac,
required this.scanResult,
required this.type,
this.macA = '',
this.macB = '',
});
factory BlueToothDataModel.fromScanResult(ScanResult result,int type,
{bool bind = false, String name = '', String mac = ''}) {
// 如果外部没有传入 name则取 localName
String finalName =
name.isNotEmpty ? name : (result.advertisementData.localName ?? '');
return BlueToothDataModel(
name: finalName,
bind: bind,
mac: mac,
scanResult: result,
type:type
);
}
}