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

Flutter Bloc结合QuickBlue扫描BLE设备:如何取消扫描流?

解决Flutter Bloc中第三方BLE扫描流无法停止监听的问题

你的核心问题是quick_blue提供的全局scanResultStream无法被emit.onEach自动终止,导致StopScanEvent触发后仍在接收扫描数据,直到下一次StartScanEvent重启监听。以下是两种可行的解决方案:


方案一:手动管理流订阅(推荐)

直接通过StreamSubscription控制扫描流的监听生命周期,在停止扫描时主动取消订阅:

class BLEBloc extends Bloc<BLEEvent, BLEState> {
  final List<BlueScanResult> _scanResults = [];
  final Stream<BlueScanResult> _scanStream = QuickBlue.scanResultStream;
  StreamSubscription<BlueScanResult>? _scanSubscription;

  BLEBloc() : super(BLEInitState()) {
    on<StartScanEvent>((event, emit) async {
      // 先清理旧订阅,避免重复监听
      await _scanSubscription?.cancel();
      QuickBlue.startScan();
      _scanResults.clear();
      emit(ScanningState()); // 可选:新增扫描中状态,提升UI反馈

      // 订阅扫描流,处理设备发现逻辑
      _scanSubscription = _scanStream.listen((data) {
        // 避免重复添加同一设备(根据deviceId判断)
        if (!_scanResults.any(result => result.deviceId == data.deviceId)) {
          _scanResults.add(data);
          // 传递不可变副本,防止外部修改Bloc内部状态
          emit(DeviceFoundState(List.unmodifiable(_scanResults)));
        }
      }, onError: (error) {
        emit(ScanErrorState(error.toString())); // 可选:处理扫描错误
      });
    });

    on<StopScanEvent>((event, emit) async {
      QuickBlue.stopScan();
      // 取消订阅并置空,终止流监听
      await _scanSubscription?.cancel();
      _scanSubscription = null;
      emit(ScanFinishedState(List.unmodifiable(_scanResults))); // 可选:携带最终扫描结果
    });
  }

  @override
  Future<void> close() {
    // Bloc销毁时确保取消订阅,避免内存泄漏
    _scanSubscription?.cancel();
    return super.close();
  }
}

关键说明:

  • 用_scanSubscription变量追踪流订阅,每次启动扫描前先取消旧订阅
  • 替换emit.onEach为listen,获得手动控制订阅的能力
  • 传递扫描结果时用List.unmodifiable生成不可变列表,避免状态污染
  • 在close方法中清理订阅,防止内存泄漏

方案二:用开关流过滤扫描数据

如果更倾向于使用Bloc的流操作符,可通过BehaviorSubject作为开关,过滤不需要的扫描数据:

import 'package:rxdart/rxdart.dart';

class BLEBloc extends Bloc<BLEEvent, BLEState> {
  final List<BlueScanResult> _scanResults = [];
  final Stream<BlueScanResult> _scanStream = QuickBlue.scanResultStream;
  final BehaviorSubject<bool> _scanEnabled = BehaviorSubject.seeded(false);

  BLEBloc() : super(BLEInitState()) {
    on<StartScanEvent>((event, emit) async {
      QuickBlue.startScan();
      _scanResults.clear();
      _scanEnabled.add(true);
      emit(ScanningState());

      await emit.onEach(
        // 仅当扫描开关开启时,处理流数据
        _scanStream.where((_) => _scanEnabled.value),
        onData: (data) {
          if (!_scanResults.any(r => r.deviceId == data.deviceId)) {
            _scanResults.add(data);
            emit(DeviceFoundState(List.unmodifiable(_scanResults)));
          }
        },
      );
    });

    on<StopScanEvent>((event, emit) {
      QuickBlue.stopScan();
      // 关闭扫描开关,后续流数据会被过滤
      _scanEnabled.add(false);
      emit(ScanFinishedState(List.unmodifiable(_scanResults)));
    });
  }

  @override
  Future<void> close() {
    _scanEnabled.close();
    return super.close();
  }
}

关键说明:

  • _scanEnabled作为开关,控制扫描流数据是否被处理
  • 停止扫描时将开关置为false,后续流数据会被where操作符过滤
  • 需在close方法中关闭BehaviorSubject,避免资源泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:43:11