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

Flutter异步函数未完成即返回:BLE扫描连接函数问题排查

Flutter Reactive Ble中scanAndConnect函数提前返回的解决办法

问题描述

我编写了一个scanAndConnect()异步函数,用于扫描BLE设备并连接到指定服务ID的设备,返回Future。问题在于,尽管在flutterReactiveBle.statusStream.listen()前使用了await,但scanAndConnect()仍会直接打印99999并返回,未等待监听逻辑执行完成。

相关代码

Future scanAndConnect(Uuid serviceId, Uuid charctId) async {
    StreamSubscription<BleStatus>? bleStatusStreamSubscription;
    StreamSubscription<DiscoveredDevice>? deviceStreamSubscription;
    Stream<DiscoveredDevice> stream;
    bleStatusStreamSubscription =
        await flutterReactiveBle.statusStream.listen((bleStatus) async {
      print("new listen ${bleStatus.toString()}");
      if (bleStatus == BleStatus.ready) {
        await bleStatusStreamSubscription!.cancel();
        connectionStatus = BLEConnectionStatus.Connecting;
        stream = await flutterReactiveBle.scanForDevices(
          withServices: [serviceId],
          scanMode: ScanMode.lowLatency,
        );
      }
    });

    print("9999999");
  }

// 调用代码
Future connectToDevice() async {
  await ble.scanAndConnect(BLE_SERVICE_UUID, BLE_CHAR_UUID);
  print("Statement after await in main");
  setState(() {
    loading = false;
    print("Changing state to ${loading.toString()}");
  });
}

Xcode输出

flutter: 9999999
flutter: Statement after await in main
flutter: Changing state to false
flutter: new listen BleStatus.unknown
flutter: new listen BleStatus.ready

解决方案

问题根源

listen()方法返回的是StreamSubscription对象,不是可等待的Future,所以你加的await完全无效——函数会直接跳过监听逻辑,执行后续代码。要让函数等待监听内的逻辑完成,需要用Completer手动控制Future的完成时机。

修改步骤

  1. 导入dart:async包(如果未导入)
  2. 创建Completer实例,用于标记任务完成状态
  3. 在监听回调中,当所有核心逻辑(比如BLE就绪、扫描启动、设备连接完成)执行完毕后,调用completer.complete()
  4. 最后返回completer.future,让外部await能等待任务结束

修改后的代码示例

import 'dart:async';

Future scanAndConnect(Uuid serviceId, Uuid charctId) async {
  final completer = Completer<void>();
  StreamSubscription<BleStatus>? bleStatusStreamSubscription;
  StreamSubscription<DiscoveredDevice>? deviceStreamSubscription;
  Stream<DiscoveredDevice>? stream;

  bleStatusStreamSubscription = flutterReactiveBle.statusStream.listen((bleStatus) async {
    print("new listen ${bleStatus.toString()}");
    if (bleStatus == BleStatus.ready) {
      await bleStatusStreamSubscription!.cancel();
      connectionStatus = BLEConnectionStatus.Connecting;
      stream = flutterReactiveBle.scanForDevices(
        withServices: [serviceId],
        scanMode: ScanMode.lowLatency,
      );

      // 如果你还需要处理扫描到设备并连接的逻辑,在此处添加
      // 例如监听stream并连接到目标设备,等连接成功后再调用complete
      // deviceStreamSubscription = stream?.listen((device) {
      //   // 连接设备逻辑
      //   completer.complete();
      // });

      // 这里假设启动扫描即视为逻辑完成,根据你的实际需求调整时机
      completer.complete();
    }
  }, onError: (error) {
    // 处理错误,避免Future一直处于pending状态
    completer.completeError(error);
    bleStatusStreamSubscription?.cancel();
  });

  // 等待completer标记任务完成
  await completer.future;
  print("9999999"); // 现在这行只会在逻辑完成后打印
}

补充说明

  • 如果你的业务逻辑需要等待设备连接完成,而不只是启动扫描,那需要在扫描到目标设备、完成连接后再调用completer.complete()
  • 必须处理错误场景(比如BLE状态异常、扫描失败),调用completer.completeError(),避免外部await一直阻塞

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 00:10:23