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:
- Create a new
Completer
. - Return its
future
. - 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.