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

Flutter中bluetooth_classic包蓝牙经典设备扫描的订阅取消问题

问题解决:bluetooth_classic扫描时Stream重复监听报错

问题原因

onDeviceDiscovered()返回的是单订阅流(Single-Subscription Stream),这类流只能被监听一次,即便取消了之前的订阅,也无法复用同一个流实例再次监听,这就是再次扫描时出现Stream has already been listened to错误的核心原因。

解决方案

1. 调整订阅变量的可空性

将订阅变量声明为可空类型,方便后续重置:

StreamSubscription<Device>? _scanResultsSubscription;

2. 封装扫描启停方法,确保每次扫描获取新流

每次启动扫描前先停止已有扫描,再重新获取流并创建新订阅:

// 启动扫描
Future<void> startScan() async {
  // 先停止当前扫描(如果存在)
  await stopScan();

  // 重新获取流实例并创建新订阅
  _scanResultsSubscription = _bluetoothClassicPlugin.onDeviceDiscovered().listen(
    (event) {
      _discoveredDevices = [..._discoveredDevices, event];
    },
    onError: (err) {
      // 按需处理监听错误
    },
    onDone: () {
      // 流结束后重置订阅变量
      _scanResultsSubscription = null;
    },
  );

  // 调用包的启动扫描API(根据实际包方法调整)
  await _bluetoothClassicPlugin.startScan();
}

// 停止扫描
Future<void> stopScan() async {
  // 检查订阅是否有效,避免重复取消
  if (_scanResultsSubscription != null && !_scanResultsSubscription!.isCanceled) {
    await _scanResultsSubscription!.cancel();
    _scanResultsSubscription = null;
  }
  // 调用包的停止扫描API(根据实际包方法调整)
  await _bluetoothClassicPlugin.stopScan();
  // 清空已发现设备(可选,根据业务需求)
  _discoveredDevices.clear();
}

3. 优化dispose方法

确保页面销毁时安全取消订阅:

@override
void dispose() {
  _scanResultsSubscription?.cancel();
  super.dispose();
}

关键注意点

  • 不要复用同一个流实例,每次扫描都要通过onDeviceDiscovered()获取新的流。
  • 取消订阅后务必将_scanResultsSubscription置为null,避免后续对已取消的订阅进行操作。
  • 启停扫描都要异步处理,确保操作顺序执行,避免竞态问题。

内容的提问来源于stack exchange,提问作者Anjali Jagtap

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:14:51