The Flutter FutureBuilder
is a great example of Flutters composability with widgets. It allows us to wrap a Future
, an async operation, and easily render the three different states, loading
, error
or result
.
However, this turned out to be less straightforward than I thought it would be and I did a deep dive to figure out why.
Reloading the FutureBuilder future
Maybe you don’t care why and just want to know how? That’s fine to, this is my solution, how to properly get the FutureBuilder
to allow you to change a future and re-render a loading
state whilst the future is processing:
final builder = FutureBuilder(
future: _future,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return _buildLoader();
}
if (snapshot.hasError) {
return _buildError();
}
if (snapshot.hasData) {
return _buildDataView();
}
return _buildNoData();
});
The hasData is not reset
My initial solution was simpler, like this:
return FutureBuilder(
future: _future,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("Result: ${snapshot.data}");
}
if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Text("Loading..");
});
Problem is, the hasData
and hasError
simply aren’t reset when the future changes, only the connectionState
is. We can see this if we break out the FutureBuilder
source code:
@override
void didUpdateWidget(FutureBuilder<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.future != widget.future) {
if (_activeCallbackIdentity != null) {
_unsubscribe();
// Snapshot state is reset here
_snapshot = _snapshot.inState(ConnectionState.none);
}
_subscribe();
}
}
The full break down
I reproduced this, issue and solution in a small GitHub project so you can see the code in entirety with a running example of how it works:
https://github.com/ddikman/flutter-rerunnable-future-builder
I hope it helps!