You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
  }
}

问题分析

  1. 重复触发连接:在build方法中,只要连接状态为disconnected就调用connect,每次Widget重建都会重复触发连接,导致多次订阅BLE连接流,覆盖之前的订阅,造成状态更新混乱。
  2. 订阅管理缺失:连接时未检查是否已有活跃的订阅,多个订阅同时存在会干扰状态更新逻辑。
  3. 错误与流结束处理不完整:仅监听了连接状态变化,未处理连接超时、流结束等场景,导致异常情况下状态无法正确回退。

解决方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:35:32