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.