1054 lines
40 KiB
Dart
1054 lines
40 KiB
Dart
import 'dart:async';
|
||
|
||
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: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/theme_controller/ThemeController.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<BlueteethDevicePage> createState() => _EPageState();
|
||
}
|
||
|
||
class _EPageState extends State<BlueteethDevicePage> {
|
||
GlobalController globalController = Get.find();
|
||
UserInfoController userInfoController = Get.find();
|
||
BlueteethBindController blueteethBindController = Get.find();
|
||
ThemeController themeController = Get.find();
|
||
late FlutterBluePlus flutterBlue; // 声明 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 = ["", "", ""];
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
flutterBlue = FlutterBluePlus(); // 初始化flutterBlue实例
|
||
_checkBluetoothPermission(); // 检查蓝牙权限
|
||
Get.find<BlueteethBindController>().startStatusPolling();
|
||
}
|
||
|
||
// 检查蓝牙权限
|
||
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 {
|
||
// Android 13+ 使用新的蓝牙权限
|
||
Map<Permission, PermissionStatus> 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) {
|
||
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 = <BleDeviceData>[];
|
||
|
||
for (var r in filteredResults) {
|
||
try {
|
||
List<int> 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");
|
||
}
|
||
}
|
||
|
||
// 使用一个临时变量 lastDeviceList 来比较是否有变化
|
||
setState(() {
|
||
bool hasChanges = false;
|
||
|
||
// 如果 devicelist 长度不同或内容有差异,认为有变化
|
||
if (blueteethBindController.model.devicelist?.length !=
|
||
parsedDeviceList.length) {
|
||
hasChanges = true;
|
||
} else {
|
||
// 深度比较每个设备的属性(比如 mac, rssi)
|
||
for (int i = 0;
|
||
i < blueteethBindController.model.devicelist!.length;
|
||
i++) {
|
||
if (blueteethBindController.model.devicelist![i].mac !=
|
||
parsedDeviceList[i].mac ||
|
||
blueteethBindController.model.devicelist![i].rssi !=
|
||
parsedDeviceList[i].rssi) {
|
||
hasChanges = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果有变化,更新 devicelist 和 blelist
|
||
if (hasChanges) {
|
||
blueteethBindController.model.devicelist = parsedDeviceList;
|
||
blueteethBindController.model.blelist = filteredResults;
|
||
// blueteethBindController.updateDeviceStatus();
|
||
}
|
||
});
|
||
});
|
||
|
||
// 等待扫描完成
|
||
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<int> 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<int> 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: themeController.currentColor.sc3),
|
||
backgroundColor: themeController.currentColor.sc17,
|
||
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: [
|
||
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: 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,
|
||
// width: double.infinity,
|
||
decoration: BoxDecoration(),
|
||
child: SvgPicture.asset(
|
||
'assets/img/icon/query.svg',
|
||
fit: BoxFit.cover,
|
||
color: stringToColor("#333333"), //固定
|
||
),
|
||
),
|
||
),
|
||
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: false,
|
||
fillColor: themeController
|
||
.currentColor.sc22,
|
||
),
|
||
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: stringToColor("#333333"), //固定
|
||
),
|
||
),
|
||
Text(
|
||
'搜索',
|
||
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: Text(
|
||
'匹配出的外围设备(${blueteethBindController.model.devicelist!.length})',
|
||
style: FlutterFlowTheme.of(context)
|
||
.bodyMedium
|
||
.override(
|
||
fontFamily: 'Inter',
|
||
fontSize: 30.rpx,
|
||
letterSpacing: 0.0,
|
||
color: themeController.currentColor.sc3,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
Obx(() {
|
||
return 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<int> 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,
|
||
);
|
||
}
|