Most modern languages have some kind of support for asynchronous programming. One of the most basic APIs Dart has for async is Future. Dart’s Future class are very similar to those found in other languages.
When the asynchronous operation starts running then it returns a Future to the calling function. It says, “I’m going to have to wait on some stuff. Here’s this empty box, you hold onto that, and when I’m done, I’ll call the return statement and put some data in there for you”. You can put it in a FutureBuilder or something.
In this tutorial, I’m going to show you how to create an asynchronous operation using Future class and call in FutureBuilder.
State of the Future
Think Future objects as boxes of data. Somebody hands you once and it starts off closed. Then a little while later, it pops open, there’s either a value or an error. So those are the three states a future can be in. First, the box is closed. We call that uncompleted. Then the box opens, and it’s completed with a value or completed with error.
How to Create Async Function?
Most of the time you’re probably not going to be creating Future directly. That’s because many of the common async tasks already have libraries that generate Futures for you. Like network, communication returns a Future accessing shared preference returns a Future.
Sometimes you’ve already got the value you need, So you can just pop it right in there. Future.value()
Future.value(10);
Also has a counterpart for completing with an error, by the way. It’s called Future.error()
, and it works essentially the same way. But it takes an error object and an optional stack trace.
Future.error(Object error, [StackTrace stackTrace])
The constructor I probably use the most, though, is Future.delayed. It works just like the default one, only it waits for a specified time before running the function and completing the Future. This one all the time when creating mock network services for testing.
Future<String> getUserOrder(int orderId) {
if (orderId == 4045)
return Future.delayed(Duration(seconds: 3), () => 'Latte Macchiato');
else
return Future.delayed(Duration(seconds: 3),
() => throw Exception('Failed: Order id is invalid'));
}
I need to make sure my little loading spinner is displaying right and then goes away, somewhere, there’s a delayed future helping me out.
Async Operation using FutureBuilder
Say you have a network service that’s going to return some JSON, and you want to display it. You can use FutureBuilder. It’s a widget that comes with the Flutter SDK. You give it a Future and builder method, and it will automatically rebuild its children when the Future completes.
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<String>(
future: getUserOrder(4045),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data,style: Theme.of(context).textTheme.headline);
} else if (snapshot.hasError) {
return Text("${snapshot.error}",style: Theme.of(context).textTheme.headline);
} else {
return CircularProgressIndicator();
}
}),
],
),
),
It does that by calling its builder method, which takes a context and a snapshot of the current state of the Future. You can check the snapshot to see if the Future completed with an error and report it. Otherwise, you can check the hasData
property to see if it completed with a value. And if not, you’re still waiting. So you can output something for that as well. Even in Flutter code, you can see how those three state keeps popping up–uncompleted, completed with value, and completed with error.