Flutter Future vs Completer

Completer is a helper class for creating Future whereas Future is a Type.

All asynchronous functions return Future, but with Completer it is possible to create synchronous function that returns Future as well. Also you can chain that synch functions with then etc.

Completer object is one way process, it's not restartable. It does the job and stops.

Future<MyObject> myMethod() {
  final completer = Completer();
  completer.complete(MyObject());
  return completer.future;
}

Update:

To give an example, in one of my projects I had to get the resolution info of network images. To do that, you need something like this: https://stackoverflow.com/a/44683714/10380182

In there, as you see, after getting the image we do a resolve process which may take time even though it's not an async process. To eliminate that blocking we simply use Completer.

Also the info we need exists inside a callback, so it will be cleaner to use Completer in there. Then, we use it via FutureBuilder. You can approach different but this is very convenient way to handle.


Prefer Future over Completer

A Completer is a class that is used to create a Future from scratch. So unless you really are creating a Future from scratch you probably shouldn't be using a Completer.

How to make a Future

You can create a Future without using a Completer by using the Future's constructor:

final myFuture = Future(() {
  final result = doSomethingThatTakesTime();
  return result;
});

Using Future.then() is another way to get a Future:

Future<bool> fileContainsBear(String path) {
  return File(path).readAsString().then((contents) {
    return contents.contains('bear');
  });
}

And any async/await method returns a Future:

Future<bool> fileContainsBear(String path) async {
  var contents = await File(path).readAsString();
  return contents.contains('bear');
}

The above methods are all recommended over using a Completer:

// This is harder to read.
Future<bool> fileContainsBear(String path) {
  var completer = Completer<bool>();

  File(path).readAsString().then((contents) {
    completer.complete(contents.contains('bear'));
  });

  return completer.future;
}

How to make a Completer

But if you really do need to use a Completer, the way to do it is like this:

  1. Create a new Completer.
  2. Return its future.
  3. Tell the completer either when it is complete or when there is an error.

Here is an example:

class AsyncOperation {
  Completer _completer = new Completer();

  Future<T> doOperation() {
    _startOperation();
    return _completer.future; // Send future object back to client.
  }

  // Something calls this when the value is ready.
  void _finishOperation(T result) {
    _completer.complete(result);
  }

  // If something goes wrong, call this.
  void _errorHappened(error) {
    _completer.completeError(error);
  }
}

The code in this answer comes from the documentation and from the Effective Dart guide.

Tags:

Dart

Flutter