Flutter_reactive_ble连接流未更新连接状态问题求助
Flutter Reactive Ble + Riverpod Generator 设备连接异常问题
问题现象
扫描设备正常,但连接流程出现异常:
- 首次尝试连接目标设备时,控制台仅输出:
flutter: Device not connected flutter: Start connecting to 3d19f04a-1e9e-1406-68de-2b6085bf185c
- 第二次尝试连接时,控制台输出:
flutter: Device not connected flutter: Start connecting to 3d19f04a-1e9e-1406-68de-2b6085bf185c flutter: ConnectionState for device 3d19f...f185c : DeviceConnectionState.connecting flutter: device connecting
- 后续无任何输出,始终无法收到
DeviceConnectionState.connected的状态更新。
现有代码
View Widget 构建方法
@override Widget build(context, ref) { final bleStateUpdate = ref.watch(bleStatusMonitorProvider); final connectionStateUpdate = ref.watch(bleDeviceConnectorProvider); final connectionState = connectionStateUpdate.connectionState ?? DeviceConnectionState.disconnected; if (bleStateUpdate == BleStatus.ready) { switch (connectionState) { case DeviceConnectionState.connected: debugPrint('device connected'); break; case DeviceConnectionState.disconnected: debugPrint('Device not connected'); ref .read(bleDeviceConnectorProvider.notifier) .connect(ref.read(notioDeviceProvider)!.id.toString()); break; case DeviceConnectionState.connecting: debugPrint('device connecting'); break; case DeviceConnectionState.disconnecting: debugPrint('device disconnecting'); break; default: debugPrint('unknown connection state'); } } // ... 其他Widget代码 }
Provider 代码
// ... Imports ... @Riverpod(keepAlive: true) class BleDeviceConnector extends _$BleDeviceConnector { final FlutterReactiveBle _ble = locator.get<FlutterReactiveBle>(); StreamSubscription<ConnectionStateUpdate>? _connectionStreamSub; final Duration _connectionTimeout = const Duration(seconds: 10); @override ConnectionStateUpdate build() { return const ConnectionStateUpdate( deviceId: "", connectionState: DeviceConnectionState.disconnected, failure: null); } Future<void> connect(String deviceId) async { debugPrint('Start connecting to $deviceId'); _connectionStreamSub = _ble .connectToDevice(id: deviceId, connectionTimeout: _connectionTimeout) .listen( handleConnectionState, onError: (Object e) => debugPrint('Connecting to device $deviceId resulted in error $e'), ); } Future<void> disconnect(String deviceId) async { try { debugPrint('disconnecting to device: $deviceId'); await _connectionStreamSub?.cancel(); } on Exception catch (e, _) { debugPrint("Error disconnecting from a device: $e"); } finally { state = ConnectionStateUpdate( deviceId: deviceId, connectionState: DeviceConnectionState.disconnected, failure: null, ); } } Future<void> handleConnectionState(ConnectionStateUpdate event) async { debugPrint( 'ConnectionState for device ${event.deviceId} : ${event.connectionState}'); state = event; } Future<void> dispose() async { await _connectionStreamSub?.cancel(); } }
问题分析
- 重复触发连接:在
build方法中,只要连接状态为disconnected就调用connect,每次Widget重建都会重复触发连接,导致多次订阅BLE连接流,覆盖之前的订阅,造成状态更新混乱。 - 订阅管理缺失:连接时未检查是否已有活跃的订阅,多个订阅同时存在会干扰状态更新逻辑。
- 错误与流结束处理不完整:仅监听了连接状态变化,未处理连接超时、流结束等场景,导致异常情况下状态无法正确回退。
解决方案
1. 修改View Widget,避免重复触发连接
使用ref.listen监听状态变化,仅在必要时触发连接,而非在build方法中直接调用:
@override Widget build(context, ref) { final bleStateUpdate = ref.watch(bleStatusMonitorProvider); final connectionStateUpdate = ref.watch(bleDeviceConnectorProvider); final connectionState = connectionStateUpdate.connectionState ?? DeviceConnectionState.disconnected; // 监听连接状态变化,仅在BLE就绪且设备断开时触发一次连接 ref.listen<ConnectionStateUpdate>(bleDeviceConnectorProvider, (previous, next) { if (bleStateUpdate == BleStatus.ready && next.connectionState == DeviceConnectionState.disconnected && previous?.connectionState != DeviceConnectionState.disconnected) { ref.read(bleDeviceConnectorProvider.notifier).connect(ref.read(notioDeviceProvider)!.id.toString()); } }); switch (connectionState) { case DeviceConnectionState.connected: debugPrint('device connected'); break; case DeviceConnectionState.disconnected: debugPrint('Device not connected'); break; case DeviceConnectionState.connecting: debugPrint('device connecting'); break; case DeviceConnectionState.disconnecting: debugPrint('device disconnecting'); break; default: debugPrint('unknown connection state'); } // ... 其他Widget代码 }
2. 完善Provider的订阅与状态管理
添加订阅唯一性检查、错误处理和流结束逻辑:
@Riverpod(keepAlive: true) class BleDeviceConnector extends _$BleDeviceConnector { final FlutterReactiveBle _ble = locator.get<FlutterReactiveBle>(); StreamSubscription<ConnectionStateUpdate>? _connectionStreamSub; final Duration _connectionTimeout = const Duration(seconds: 10); String? _currentConnectedDeviceId; @override ConnectionStateUpdate build() { return const ConnectionStateUpdate( deviceId: "", connectionState: DeviceConnectionState.disconnected, failure: null, ); } Future<void> connect(String deviceId) async { // 避免重复连接同一设备 if (_currentConnectedDeviceId == deviceId && _connectionStreamSub != null) return; debugPrint('Start connecting to $deviceId'); // 先取消之前的订阅 await _connectionStreamSub?.cancel(); _currentConnectedDeviceId = deviceId; _connectionStreamSub = _ble .connectToDevice(id: deviceId, connectionTimeout: _connectionTimeout) .listen( handleConnectionState, onError: (Object e) { debugPrint('Connecting to device $deviceId resulted in error $e'); // 错误发生时更新状态为断开 state = ConnectionStateUpdate( deviceId: deviceId, connectionState: DeviceConnectionState.disconnected, failure: e as BleError?, ); _currentConnectedDeviceId = null; }, onDone: () { debugPrint('Connection stream done for device $deviceId'); // 流结束时更新状态为断开 state = ConnectionStateUpdate( deviceId: deviceId, connectionState: DeviceConnectionState.disconnected, failure: null, ); _currentConnectedDeviceId = null; }, ); } Future<void> disconnect(String deviceId) async { try { debugPrint('disconnecting to device: $deviceId'); await _connectionStreamSub?.cancel(); } on Exception catch (e, _) { debugPrint("Error disconnecting from a device: $e"); } finally { _currentConnectedDeviceId = null; state = ConnectionStateUpdate( deviceId: deviceId, connectionState: DeviceConnectionState.disconnected, failure: null, ); } } Future<void> handleConnectionState(ConnectionStateUpdate event) async { debugPrint('ConnectionState for device ${event.deviceId} : ${event.connectionState}'); state = event; // 更新当前连接设备ID if (event.connectionState == DeviceConnectionState.connected) { _currentConnectedDeviceId = event.deviceId; } else if (event.connectionState == DeviceConnectionState.disconnected) { _currentConnectedDeviceId = null; } } @override void dispose() { _connectionStreamSub?.cancel(); super.dispose(); } }
内容的提问来源于stack exchange,提问作者Nissaba
相关产品推荐
相关产品推荐

