Commit 192f8476 authored by akari's avatar akari

fix: 修复蓝牙相关

parent d697b832
...@@ -28,19 +28,26 @@ class LakiMqttConfig { ...@@ -28,19 +28,26 @@ class LakiMqttConfig {
this.logging = false, this.logging = false,
}); });
/// TODO: 替换为腾讯云 IoT MQTT broker 地址、端口、clientId 和鉴权信息。 factory LakiMqttConfig.tencentTdmq({
const LakiMqttConfig.placeholder() required String deviceSn,
: host = '', bool logging = false,
port = 1883, }) {
clientId = '', return LakiMqttConfig(
username = null, host: _tencentTdmqHost,
password = null, port: _tencentTdmqPort,
useSsl = false, clientId: '$_tencentTdmqClientIdPrefix$deviceSn',
useWebSocket = false, username: _tencentTdmqUsername,
webSocketProtocols = null, password: _tencentTdmqPassword,
keepAliveSeconds = 30, logging: logging,
connectTimeoutMs = 5000, );
logging = false; }
static const String _tencentTdmqHost =
'mqtt-7jqojva8-sh-public.mqtt.tencenttdmq.com';
static const int _tencentTdmqPort = 1883;
static const String _tencentTdmqClientIdPrefix = 'GID_icu@@@';
static const String _tencentTdmqUsername = 'root';
static const String _tencentTdmqPassword = 'sk665f2b219cb3700b';
final String host; final String host;
final int port; final int port;
......
...@@ -6,6 +6,8 @@ import 'package:laki_icu_app/enums/video_stream_mode_enum.dart'; ...@@ -6,6 +6,8 @@ import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/services/monitoring_service.dart'; import 'package:laki_icu_app/services/monitoring_service.dart';
import 'package:laki_icu_app/services/p2p_video_service.dart'; import 'package:laki_icu_app/services/p2p_video_service.dart';
import 'package:laki_icu_app/services/webrtc_service.dart'; import 'package:laki_icu_app/services/webrtc_service.dart';
import 'package:laki_icu_app/utils/bluetooth/index.dart';
import 'package:laki_icu_app/utils/storage/storage_service.dart';
import 'monitoring_index_state.dart'; import 'monitoring_index_state.dart';
...@@ -16,9 +18,16 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> { ...@@ -16,9 +18,16 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
final MonitoringService _monitoringService; final MonitoringService _monitoringService;
final WebrtcService _webrtcService; final WebrtcService _webrtcService;
final P2pVideoService _p2pVideoService; final P2pVideoService _p2pVideoService;
final StorageService _storageService;
final BleBluetoothManager _bluetoothManager;
final McuReportFrameDecoder _mcuReportDecoder;
StreamSubscription<WebrtcConnectionState>? _webrtcStateSub; StreamSubscription<WebrtcConnectionState>? _webrtcStateSub;
StreamSubscription<P2pServiceState>? _p2pStateSub; StreamSubscription<P2pServiceState>? _p2pStateSub;
StreamSubscription<List<BluetoothScanDevice>>? _bluetoothScanSub;
StreamSubscription<BluetoothConnectionStatus>? _bluetoothStateSub;
StreamSubscription<String>? _bluetoothMessageSub;
StreamSubscription<BluetoothDataPacket>? _bluetoothDataSub;
/// 连接代际计数器 —— 每次切换模式时 +1, /// 连接代际计数器 —— 每次切换模式时 +1,
/// 防止旧连接的异步结果污染当前模式的状态。 /// 防止旧连接的异步结果污染当前模式的状态。
...@@ -28,9 +37,14 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> { ...@@ -28,9 +37,14 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
: _monitoringService = MonitoringService(), : _monitoringService = MonitoringService(),
_webrtcService = WebrtcService(), _webrtcService = WebrtcService(),
_p2pVideoService = P2pVideoService(), _p2pVideoService = P2pVideoService(),
_storageService = StorageService(),
_bluetoothManager = BleBluetoothManager(),
_mcuReportDecoder = McuReportFrameDecoder(),
super(const MonitoringIndexState()) { super(const MonitoringIndexState()) {
_listenWebrtcState(); _listenWebrtcState();
_listenP2pState(); _listenP2pState();
_listenBluetoothState();
_loadBoundBluetoothDevice();
loadData(); loadData();
} }
...@@ -47,11 +61,69 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> { ...@@ -47,11 +61,69 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
_p2pStateSub = _p2pVideoService.stateStream.listen((state) { _p2pStateSub = _p2pVideoService.stateStream.listen((state) {
if (isClosed) return; if (isClosed) return;
emit(this.state.copyWith( emit(this.state.copyWith(
isP2pConnected: state == P2pServiceState.connected, isP2pConnected: state == P2pServiceState.connected,
)); ));
}); });
} }
/// 监听蓝牙扫描、连接和数据状态
void _listenBluetoothState() {
_bluetoothScanSub = _bluetoothManager.scanDevicesStream.listen((devices) {
if (isClosed) return;
emit(state.copyWith(bluetoothDevices: devices));
});
_bluetoothStateSub = _bluetoothManager.stateStream.listen((status) {
if (isClosed) return;
emit(state.copyWith(bluetoothConnectionStatus: status));
});
_bluetoothMessageSub = _bluetoothManager.messageStream.listen((message) {
if (isClosed) return;
emit(state.copyWith(bluetoothMessage: message));
});
_bluetoothDataSub = _bluetoothManager.dataStream.listen((packet) {
if (isClosed) return;
try {
final reports = _mcuReportDecoder.addBytes(packet.bytes);
final latestReport = reports.isEmpty ? null : reports.last;
emit(state.copyWith(
latestBluetoothRawHex: packet.rawHex,
latestMcuReport: latestReport,
bluetoothMessage: latestReport == null
? state.bluetoothMessage
: '蓝牙数据解析成功: ${latestReport.sn.isEmpty ? latestReport.head : latestReport.sn}',
));
} catch (e) {
emit(state.copyWith(
latestBluetoothRawHex: packet.rawHex,
bluetoothMessage: '蓝牙数据解析失败: $e',
));
}
});
}
Future<void> _loadBoundBluetoothDevice() async {
final boundDevice = await _storageService.getBoundBluetoothDevice();
if (isClosed) return;
final deviceId = boundDevice['deviceId'];
if (deviceId != null && deviceId.isNotEmpty) {
final userMac = await _storageService.getUserMac();
if (userMac == null || userMac.isEmpty) {
await _storageService.saveUserMac(deviceId);
}
} else {
await _storageService.getUserMac();
}
if (isClosed) return;
emit(state.copyWith(
boundBluetoothDeviceId:
deviceId == null || deviceId.isEmpty ? null : deviceId,
boundBluetoothDeviceName: boundDevice['deviceName'],
));
}
Future<void> loadData() async { Future<void> loadData() async {
emit(state.copyWith( emit(state.copyWith(
status: MonitoringIndexStatus.loading, status: MonitoringIndexStatus.loading,
...@@ -204,6 +276,81 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> { ...@@ -204,6 +276,81 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
} }
} }
// ==================== 蓝牙扫描/绑定 ====================
Future<void> scanBluetoothDevices() async {
if (state.isBluetoothScanning) return;
emit(state.copyWith(
isBluetoothScanning: true,
bluetoothDevices: const [],
bluetoothMessage: '开始扫描蓝牙设备',
));
try {
await _bluetoothManager.scanForDevices(
namePrefixes: const [],
timeout: const Duration(seconds: 10),
);
} catch (e) {
if (isClosed) return;
emit(state.copyWith(bluetoothMessage: '蓝牙扫描失败: $e'));
} finally {
if (!isClosed) {
emit(state.copyWith(isBluetoothScanning: false));
}
}
}
Future<void> bindBluetoothDevice(BluetoothScanDevice device) async {
if (state.isBluetoothBinding) return;
_mcuReportDecoder.clear();
emit(state.copyWith(
isBluetoothBinding: true,
bindingBluetoothDeviceId: device.remoteId,
bluetoothMessage: '正在绑定蓝牙设备: ${device.name}',
));
try {
await _bluetoothManager.connectToDevice(device);
await _storageService.saveBoundBluetoothDevice(
deviceId: device.remoteId,
deviceName: device.name,
);
await _storageService.saveUserMac(device.remoteId);
if (isClosed) return;
emit(state.copyWith(
isBluetoothBinding: false,
bindingBluetoothDeviceId: '',
boundBluetoothDeviceId: device.remoteId,
boundBluetoothDeviceName: device.name,
bluetoothMessage: '蓝牙设备已绑定: ${device.name}',
));
} catch (e) {
if (isClosed) return;
emit(state.copyWith(
isBluetoothBinding: false,
bindingBluetoothDeviceId: '',
bluetoothMessage: '蓝牙绑定失败: $e',
));
}
}
Future<void> unbindBluetoothDevice() async {
await _bluetoothManager.disconnect();
await _storageService.deleteBoundBluetoothDevice();
await _storageService.deleteUserMac();
_mcuReportDecoder.clear();
if (isClosed) return;
emit(state.copyWith(
clearBoundBluetoothDevice: true,
clearLatestMcuReport: true,
bluetoothMessage: '蓝牙设备已解绑',
latestBluetoothRawHex: '',
));
}
/// 获取 WebRTC 渲染器供 UI 层使用 /// 获取 WebRTC 渲染器供 UI 层使用
WebrtcService get webrtcService => _webrtcService; WebrtcService get webrtcService => _webrtcService;
...@@ -214,8 +361,12 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> { ...@@ -214,8 +361,12 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
Future<void> close() { Future<void> close() {
_webrtcStateSub?.cancel(); _webrtcStateSub?.cancel();
_p2pStateSub?.cancel(); _p2pStateSub?.cancel();
_bluetoothScanSub?.cancel();
_bluetoothStateSub?.cancel();
_bluetoothMessageSub?.cancel();
_bluetoothDataSub?.cancel();
_webrtcService.dispose(); _webrtcService.dispose();
_p2pVideoService.dispose(); _p2pVideoService.dispose();
return super.close(); return _bluetoothManager.release().then((_) => super.close());
} }
} }
...@@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart'; ...@@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart';
import 'package:laki_icu_app/enums/video_stream_mode_enum.dart'; import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart'; import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'package:laki_icu_app/services/webrtc_service.dart'; import 'package:laki_icu_app/services/webrtc_service.dart';
import 'package:laki_icu_app/utils/bluetooth/index.dart';
enum MonitoringIndexStatus { enum MonitoringIndexStatus {
initial, initial,
...@@ -31,6 +32,36 @@ class MonitoringIndexState extends Equatable { ...@@ -31,6 +32,36 @@ class MonitoringIndexState extends Equatable {
/// P2P 模式是否已连接 /// P2P 模式是否已连接
final bool isP2pConnected; final bool isP2pConnected;
/// 蓝牙扫描中
final bool isBluetoothScanning;
/// 蓝牙绑定/连接处理中
final bool isBluetoothBinding;
/// 正在绑定/连接的蓝牙设备 ID
final String bindingBluetoothDeviceId;
/// 扫描到的蓝牙设备
final List<BluetoothScanDevice> bluetoothDevices;
/// 已绑定蓝牙设备 ID
final String? boundBluetoothDeviceId;
/// 已绑定蓝牙设备名称
final String? boundBluetoothDeviceName;
/// 蓝牙连接状态
final BluetoothConnectionStatus bluetoothConnectionStatus;
/// 蓝牙状态提示
final String? bluetoothMessage;
/// 最近收到的蓝牙原始 Hex 数据
final String? latestBluetoothRawHex;
/// 最近解析出的 MCU 上报数据
final McuReport? latestMcuReport;
const MonitoringIndexState({ const MonitoringIndexState({
this.status = MonitoringIndexStatus.initial, this.status = MonitoringIndexStatus.initial,
this.isLoading = false, this.isLoading = false,
...@@ -43,6 +74,16 @@ class MonitoringIndexState extends Equatable { ...@@ -43,6 +74,16 @@ class MonitoringIndexState extends Equatable {
this.videoStreamMode = VideoStreamMode.p2p, this.videoStreamMode = VideoStreamMode.p2p,
this.isSwitchingMode = false, this.isSwitchingMode = false,
this.isP2pConnected = false, this.isP2pConnected = false,
this.isBluetoothScanning = false,
this.isBluetoothBinding = false,
this.bindingBluetoothDeviceId = '',
this.bluetoothDevices = const [],
this.boundBluetoothDeviceId,
this.boundBluetoothDeviceName,
this.bluetoothConnectionStatus = BluetoothConnectionStatus.disconnected,
this.bluetoothMessage,
this.latestBluetoothRawHex,
this.latestMcuReport,
}); });
MonitoringIndexState copyWith({ MonitoringIndexState copyWith({
...@@ -57,6 +98,18 @@ class MonitoringIndexState extends Equatable { ...@@ -57,6 +98,18 @@ class MonitoringIndexState extends Equatable {
VideoStreamMode? videoStreamMode, VideoStreamMode? videoStreamMode,
bool? isSwitchingMode, bool? isSwitchingMode,
bool? isP2pConnected, bool? isP2pConnected,
bool? isBluetoothScanning,
bool? isBluetoothBinding,
String? bindingBluetoothDeviceId,
List<BluetoothScanDevice>? bluetoothDevices,
String? boundBluetoothDeviceId,
String? boundBluetoothDeviceName,
BluetoothConnectionStatus? bluetoothConnectionStatus,
String? bluetoothMessage,
String? latestBluetoothRawHex,
McuReport? latestMcuReport,
bool clearBoundBluetoothDevice = false,
bool clearLatestMcuReport = false,
}) { }) {
return MonitoringIndexState( return MonitoringIndexState(
status: status ?? this.status, status: status ?? this.status,
...@@ -66,11 +119,28 @@ class MonitoringIndexState extends Equatable { ...@@ -66,11 +119,28 @@ class MonitoringIndexState extends Equatable {
patientInfo: patientInfo ?? this.patientInfo, patientInfo: patientInfo ?? this.patientInfo,
alerts: alerts ?? this.alerts, alerts: alerts ?? this.alerts,
menuItems: menuItems ?? this.menuItems, menuItems: menuItems ?? this.menuItems,
videoConnectionState: videoConnectionState: videoConnectionState ?? this.videoConnectionState,
videoConnectionState ?? this.videoConnectionState,
videoStreamMode: videoStreamMode ?? this.videoStreamMode, videoStreamMode: videoStreamMode ?? this.videoStreamMode,
isSwitchingMode: isSwitchingMode ?? this.isSwitchingMode, isSwitchingMode: isSwitchingMode ?? this.isSwitchingMode,
isP2pConnected: isP2pConnected ?? this.isP2pConnected, isP2pConnected: isP2pConnected ?? this.isP2pConnected,
isBluetoothScanning: isBluetoothScanning ?? this.isBluetoothScanning,
isBluetoothBinding: isBluetoothBinding ?? this.isBluetoothBinding,
bindingBluetoothDeviceId:
bindingBluetoothDeviceId ?? this.bindingBluetoothDeviceId,
bluetoothDevices: bluetoothDevices ?? this.bluetoothDevices,
boundBluetoothDeviceId: clearBoundBluetoothDevice
? null
: boundBluetoothDeviceId ?? this.boundBluetoothDeviceId,
boundBluetoothDeviceName: clearBoundBluetoothDevice
? null
: boundBluetoothDeviceName ?? this.boundBluetoothDeviceName,
bluetoothConnectionStatus:
bluetoothConnectionStatus ?? this.bluetoothConnectionStatus,
bluetoothMessage: bluetoothMessage ?? this.bluetoothMessage,
latestBluetoothRawHex:
latestBluetoothRawHex ?? this.latestBluetoothRawHex,
latestMcuReport:
clearLatestMcuReport ? null : latestMcuReport ?? this.latestMcuReport,
); );
} }
...@@ -87,5 +157,15 @@ class MonitoringIndexState extends Equatable { ...@@ -87,5 +157,15 @@ class MonitoringIndexState extends Equatable {
videoStreamMode, videoStreamMode,
isSwitchingMode, isSwitchingMode,
isP2pConnected, isP2pConnected,
isBluetoothScanning,
isBluetoothBinding,
bindingBluetoothDeviceId,
bluetoothDevices,
boundBluetoothDeviceId,
boundBluetoothDeviceName,
bluetoothConnectionStatus,
bluetoothMessage,
latestBluetoothRawHex,
latestMcuReport,
]; ];
} }
...@@ -7,6 +7,7 @@ import 'package:laki_icu_app/models/bo/monitoring_bo.dart'; ...@@ -7,6 +7,7 @@ import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'cubit/monitoring_index_cubit.dart'; import 'cubit/monitoring_index_cubit.dart';
import 'cubit/monitoring_index_state.dart'; import 'cubit/monitoring_index_state.dart';
import 'widgets/bluetooth_bind_dialog.dart';
import 'widgets/monitoring_alert_card.dart'; import 'widgets/monitoring_alert_card.dart';
import 'widgets/monitoring_bottom_menu.dart'; import 'widgets/monitoring_bottom_menu.dart';
import 'widgets/monitoring_metric_card.dart'; import 'widgets/monitoring_metric_card.dart';
...@@ -95,24 +96,25 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -95,24 +96,25 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
Container( Container(
height: topHeight.clamp(80.0, 104.0), height: topHeight.clamp(80.0, 104.0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: BoxBorder.all(width: 1.w,color: Colors.red) border: BoxBorder.all(width: 1.w, color: Colors.red)),
), child: MonitoringTopStatusBar(
child: const MonitoringTopStatusBar(), isBluetoothBound:
state.boundBluetoothDeviceId?.isNotEmpty == true,
onBluetoothTap: _showBluetoothDialog,
),
), ),
Expanded( Expanded(
child: Container( child: Container(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
decoration: BoxDecoration( decoration: BoxDecoration(
border: BoxBorder.all(width: 1.w,color: Colors.red) border: BoxBorder.all(width: 1.w, color: Colors.red)),
),
child: _buildMainArea(state, gap), child: _buildMainArea(state, gap),
), ),
), ),
Container( Container(
height: bottomHeight.clamp(92.0, 134.0), height: bottomHeight.clamp(92.0, 134.0),
decoration: BoxDecoration( decoration: BoxDecoration(
border: BoxBorder.all(width: 1.w,color: Colors.red) border: BoxBorder.all(width: 1.w, color: Colors.red)),
),
child: MonitoringBottomMenu(menuItems: state.menuItems), child: MonitoringBottomMenu(menuItems: state.menuItems),
), ),
], ],
...@@ -142,7 +144,6 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -142,7 +144,6 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
child: _buildMetrics(state.metrics, gap), child: _buildMetrics(state.metrics, gap),
), ),
// SizedBox(width: gap), // SizedBox(width: gap),
], ],
), ),
), ),
...@@ -190,8 +191,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -190,8 +191,7 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
child: Container( child: Container(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
decoration: BoxDecoration( decoration: BoxDecoration(
border: BoxBorder.all(width: 1.w,color: Colors.red) border: BoxBorder.all(width: 1.w, color: Colors.red)),
),
// padding: EdgeInsets.only( // padding: EdgeInsets.only(
// right: metric == displayMetrics.last ? 0 : gap, // right: metric == displayMetrics.last ? 0 : gap,
// ), // ),
...@@ -213,6 +213,16 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -213,6 +213,16 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
cubit.switchStreamMode(nextMode); cubit.switchStreamMode(nextMode);
} }
void _showBluetoothDialog() {
showDialog<void>(
context: context,
builder: (_) => BlocProvider.value(
value: _monitoringIndexCubit,
child: const BluetoothBindDialog(),
),
);
}
List<MonitoringMetricBO> _environmentMetrics( List<MonitoringMetricBO> _environmentMetrics(
List<MonitoringMetricBO> metrics) { List<MonitoringMetricBO> metrics) {
final matched = metrics final matched = metrics
......
This diff is collapsed.
...@@ -2,7 +2,14 @@ import 'package:flutter/material.dart'; ...@@ -2,7 +2,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
class MonitoringTopStatusBar extends StatelessWidget { class MonitoringTopStatusBar extends StatelessWidget {
const MonitoringTopStatusBar({super.key}); const MonitoringTopStatusBar({
super.key,
this.isBluetoothBound = false,
this.onBluetoothTap,
});
final bool isBluetoothBound;
final VoidCallback? onBluetoothTap;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -13,15 +20,15 @@ class MonitoringTopStatusBar extends StatelessWidget { ...@@ -13,15 +20,15 @@ class MonitoringTopStatusBar extends StatelessWidget {
fit: BoxFit.fill, fit: BoxFit.fill,
), ),
), ),
padding: EdgeInsets.only(left: 20.w,bottom: 22.h), padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 22.h),
child: Row( child: Row(
children: [ children: [
// SizedBox(width: 18.w), // SizedBox(width: 18.w),
Text( Text(
'总运行时长:9999h', '总运行时长:9999h',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16.sp, fontSize: 16.sp,
), ),
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
...@@ -29,7 +36,7 @@ class MonitoringTopStatusBar extends StatelessWidget { ...@@ -29,7 +36,7 @@ class MonitoringTopStatusBar extends StatelessWidget {
'环境温度:30.68', '环境温度:30.68',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16.sp, fontSize: 16.sp,
), ),
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
...@@ -37,14 +44,64 @@ class MonitoringTopStatusBar extends StatelessWidget { ...@@ -37,14 +44,64 @@ class MonitoringTopStatusBar extends StatelessWidget {
'总制氧时长:500h', '总制氧时长:500h',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 16.sp, fontSize: 16.sp,
), ),
), ),
// const Spacer(), const Spacer(),
_BluetoothStatusButton(
isActive: isBluetoothBound,
onTap: onBluetoothTap,
),
], ],
), ),
); );
} }
}
class _BluetoothStatusButton extends StatelessWidget {
const _BluetoothStatusButton({
required this.isActive,
required this.onTap,
});
final bool isActive;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
final foregroundColor = isActive ? const Color(0xFF003A8C) : Colors.white;
final backgroundColor = isActive
? const Color(0xFF00F6FF)
: Colors.white.withValues(alpha: 0.08);
final borderColor = isActive ? const Color(0xFFB8FFFF) : Colors.white24;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(18.r),
child: Container(
height: 34.h,
padding: EdgeInsets.symmetric(horizontal: 12.w),
decoration: BoxDecoration(
color: backgroundColor,
border: Border.all(color: borderColor, width: 1),
borderRadius: BorderRadius.circular(18.r),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.bluetooth, color: foregroundColor, size: 18.w),
SizedBox(width: 6.w),
Text(
'TSAAS',
style: TextStyle(
color: foregroundColor,
fontSize: 14.sp,
fontWeight: isActive ? FontWeight.w700 : FontWeight.w400,
),
),
],
),
),
);
}
} }
...@@ -59,6 +59,7 @@ dependencies: ...@@ -59,6 +59,7 @@ dependencies:
path: ./plugins/vsdk path: ./plugins/vsdk
flutter_reactive_ble: ^5.5.0 flutter_reactive_ble: ^5.5.0
mqtt_client: ^10.5.1 mqtt_client: ^10.5.1
marionette_flutter: ^0.5.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment