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!
Worked like a charm! Thanks for this post.
I’m happy to hear that, thank you too!
Thank you!!!
Thank you for reading, glad it helps!
such a subtle error, im sure its been made a million times, +rep
It’s really not obvious is it 🙂
Thanks dude, I was already following the same approach as you but seeing you use it gave me confidence that the issue was somewhere else, and it’s finally working now 🙂
Happy it helped! This is pretty dated by now though. Recently, I’ve been picking up Riverpod and with the FutureProvider you get this really neat syntax of handling the different states which completely avoids this issue.