Ryan Moeller
Ryan Moeller

Reputation: 151

Dart Async Does Not Wait for Completion

I'm currently trying to wait for a BLE connection to result in one of two outcomes:

Instead of returning a true or false value as desired, null is immediately returned, without waiting for the function to finish.

I'm using dart's Future and async functionality in order to wait for the completion of the connect function. Here is my code below:

BLE Connect method:

    static Future<bool> connect(BluetoothDevice d) async {
    // Connect to device
    Duration timeout = new Duration(seconds: 5);

    deviceConnection = _flutterBlue.connect(d, timeout: timeout).listen((s) {
      deviceState = s;
      if (s == BluetoothDeviceState.connected) {
        device = d;

        device.discoverServices().then((s) {
          ... Some service discovery stuff ...
        });
      }
    }, onDone: () {
      return deviceState == BluetoothDeviceState.connected;
    });
  }

Where the connect method is being called from:

bool isConnected = await FlutterBLE.connect(device);

if(isConnected) {
    ... Do some stuff ...
} else {
    ... Do some other stuff ...
}

What am I doing wrong here?

Upvotes: 4

Views: 6383

Answers (2)

lrn
lrn

Reputation: 71623

As Günther Zöchbauer has pointed out, the mistake is in the onDone part. You are returning a value there that nobody will ever see, and you are no returning anything from the surrounding function.

You are inside an async function, so you can use await for to iterate the stream. You also want to stop listening to the stream the first time you get a connection event, because you only care about the first connection. The stream of connection events itself never stops.

static Future<bool> connect(BluetoothDevice d) async {
  // Connect to device
  Duration timeout = const Duration(seconds: 5);

  await for (var s in _flutterBlue.connect(d, timeout: timeout)) {
    deviceState = s;
    if (s == BluetoothDeviceState.connected) {
      device = d;

      device.discoverServices().then((s) {
        ... Some service discovery stuff ...
      });
      return true;
    }
  }
  // The stream ended somehow, there will be no further events.
  return false;
}

If you don't want to use await for (and not using an async function to begin with), I would recommend using firstWhere to find the first connection (if any) over listen:

static Future<bool> connect(BluetoothDevice d) {
  // Connect to device
  Duration timeout = const Duration(seconds: 5);

  return _flutterBlue.connect(d, timeout: timeout).firstWhere((s) {
    return s == BluetoothDeviceState.connected;
  }, orElse: () => null).then((s) {
    if (s == null) return false;
    deviceState = s;
    device = d;
    device.discoverServices().then((s) {
      //... Some service discovery stuff ...
    });
    return true;
  });

It's also slightly suspicious that nobody waits for the future returned by device.discoverServices().then(...). Make sure that this is correct.

Upvotes: 3

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657108

The onDone part doesn't do what you expect.
Try instead:

static Future<bool> connect(BluetoothDevice d) async {
// Connect to device
Duration timeout = new Duration(seconds: 5);

await _flutterBlue.connect(d, timeout: timeout).listen((s) {
  deviceState = s;
  if (s == BluetoothDeviceState.connected) {
    device = d;

    device.discoverServices().then((s) {
      ... Some service discovery stuff ...
    });
  }
}).asFuture();
return deviceState == BluetoothDeviceState.connected;
}

Upvotes: 0

Related Questions