When we should use FutureBuilder Widget in Flutter
In Flutter, the FutureBuilder Widget is used to create widgets based on the most recent interaction with a Future. This widget relies on a previously obtained Future, usually through a state change or updates in dependencies. The FutureBuilder executes an asynchronous function and updates the UI based on the result of that function. Additionally, FutureBuilder is inherently Stateful, meaning it manages its own state similar to how StatefulWidgets do.
Usage of FutureBuilder
The FutureBuilder widget in Flutter is best used when you have an asynchronous operation, like fetching data from a network or a database, and you want to update your UI based on the result of that operation. It helps in managing the asynchronous flow by handling loading states, errors, and displaying data once it’s available, making it ideal for scenarios where you’re working with Futures.
FutureBuilder removes boilerplate code.
Let’s say you want to fetch some data from the backend on page launch and show a loader until data comes. And display that data in ListView.
Tasks for ListBuilder:
- Have two state variables, dataFromBackend and isLoadingFlag.
- On launch, set isLoadingFlag = true, and based on this, show loader.
- Once data arrives, set data with what you get from backend and set isLoadingFlag = false (inside setState obviously).
- We need to have a if-else in widget creation. If isLoadingFlag is true, show the loader else show the data. On failure, show error message.
Tasks for FutureBuilder:
- Give the async task in future of Future Builder.
- Based on connectionState, show message (loading, active(streams), done).
- Based on data(snapshot.hasError), show view.
Pros of FutureBuilder
- Does not use the two state variables and setState
- Reactive programming (This will take care of updating the view on data arrival)
FutureBuilder<String>(
future: _fetchNetworkCall, // async work
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return Text('Loading....');
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return Text('Result: ${snapshot.data}');
}
},
)
Performance impact:
I just looked into the FutureBuilder code to understand the performance impact of using this.
- This is just a StatefulWidget whose state variable is _snapshot
- Initial state is _snapshot = AsyncSnapshot.withData(ConnectionState.none, widget.initialData);
- It is subscribing to future which we send via the constructor and update the state based on that.
Example
widget.future.then<void>((T data) {
if (_activeCallbackIdentity == callbackIdentity) {
setState(() {
_snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
});
}
}, onError: (Object error) {
if (_activeCallbackIdentity == callbackIdentity) {
setState(() {
_snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error);
});
}
});
So the FutureBuilder is a wrapper/boilerplate of what we do typically, hence there should not be any performance impact.
You can also read how to generate signed apk in Flutter