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
相关产品推荐
相关产品推荐

