Bidev
AdvancedAsync Programming

What is a Completer in Dart and when would you use one?

Short Answer

A Completer lets you manually create and control a Future, resolving it later with .complete() or .completeError() — useful for wrapping callback-based APIs into a Future-based one.

Normally you get a Future by calling an async function, but sometimes you need to bridge a callback-style API (a native plugin, an event listener) into Future-based code. Completer<T>() gives you a .future property you can return immediately, and separately, you call .complete(value) (or .completeError(error)) once, whenever the actual result becomes available.

This pattern is common when wrapping platform channel callbacks, converting a one-off Stream event into a Future, or adding a timeout race to an operation that doesn't natively support one.

Code Example

Future<String> waitForCallback() {
  final completer = Completer<String>();
  someCallbackApi.onResult((result) {
    completer.complete(result);
  });
  return completer.future;
}

Common Mistakes

  • ×Calling .complete() more than once on the same Completer — this throws a StateError, since a Future can only resolve once.
  • ×Using a Completer when a plain async function would do.