At the time of this writing (September 19, 2019), there’s a lot of bad information in books and on the internet about how to use a Flutter FutureBuilder
. That bad information caused me to waste a lot of time over the last two days. I don’t have time today to explain everything, but in short, here’s a technically correct FutureBuilder
example:
import 'package:flutter/material.dart';
import 'shared_prefs_helper.dart';
class PreferencesEnableNotifications extends StatefulWidget {
@override
PreferencesEnableNotificationsState createState() => PreferencesEnableNotificationsState();
}
class PreferencesEnableNotificationsState extends State<PreferencesEnableNotifications> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Enable Notifications"),
),
body: FutureBuilder<bool>(
future: SharedPreferencesHelper.getEnableNotifications(), //returns bool
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// YOUR CUSTOM CODE GOES HERE
return CheckboxListTile(
title: const Text('Enable Notifications'),
value: snapshot.data, //the bool in the SharedPreferences
onChanged: (val) {
setState(() {
SharedPreferencesHelper.setEnableNotifications(val);
});
}
);
} else {
return new CircularProgressIndicator();
}
}
)
);
}
}
The solution
The key part of this proper solution is everything inside of the “builder:
” code. The most important thing to do is to show something like the CircularProgressIndicator
while the FutureBuilder
is waiting for the data to arrive. In this example the data comes from a Flutter SharedPreference, specifically this method which returns a Future
:
future: SharedPreferencesHelper.getEnableNotifications(),
Why this is important
If you don’t write the FutureBuilder
code as shown, you’ll be in for a world of hurt when your data is returned slowly (in my case, the data from the SharedPreferencesHelper.getEnableNotifications()
method). If your data comes back in 10ms or so, you might be fine without the code shown, but if/when your data comes back more slowly, you’ll get some really ugly exceptions written in yellow text on a red background if you don’t use the code shown. (Just use the code shown. Always.)
I’ll try to write more when I have time, but in short, if you want to create a Flutter FutureBuilder
that runs without errors when your data is returned slowly, I can confirm that this approach works successfully.
Update: Still not 100% correct
As I tried to do more complicated things with my app, I found out that the solution above is still not 100% correct, even though it’s much better than other documentation that’s out there.
The big thing I have learned is that you should not directly call a function in the “future: ...
” part of the code. Because your build method may be called many times — think 60 times a second — if you call a function in that area, you’ll end up calling that function many times. Instead, what you’re supposed to do is create your future as a class-level variable in a State
method like initState
.
I’ll show how to do this when I have more time, but this is actually an extremely important point. I found this out the hard way when I started adding some animation into my build
method, and suddenly the function shown in the original code above was being called many times, instead of just once.