I was wondering when I should use the future builder. For example, if I want to make an http request and show the results in a list view, as soon as you open the view, shoul
FutureBuilder Example
When you want to rander widget after async call then use FutureBuilder()
class _DemoState extends State<Demo> {
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: downloadData(), // function where you call your api
builder: (BuildContext context, AsyncSnapshot<String> snapshot) { // AsyncSnapshot<Your object type>
if( snapshot.connectionState == ConnectionState.waiting){
return Center(child: Text('Please wait its loading...'));
}else{
if (snapshot.hasError)
return Center(child: Text('Error: ${snapshot.error}'));
else
return Center(child: new Text('${snapshot.data}')); // snapshot.data :- get your object which is pass from your downloadData() function
}
},
);
}
Future<String> downloadData()async{
// var response = await http.get('https://getProjectList');
return Future.value("Data download successfully"); // return your response
}
}
In future builder, it calls the future function to wait for the result, and as soon as it produces the result it calls the builder function where we build the widget.
AsyncSnapshot has 3 state:
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.
Tasks for ListBuilder:
dataFromBackend and isLoadingFlagisLoadingFlag = true, and based on this, show loader.isLoadingFlag = false (inside setState obviously)if-else in widget creation. If isLoadingFlag is true, show the loader else show the data. On failure, show error message.Tasks for FutureBuilder:
future of Future BuilderconnectionState, show message (loading, active(streams), done)data(snapshot.hasError), show viewPros of FutureBuilder
setStateFutureBuilder will take care of updating the view on data arrival)Example:
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.
StatefulWidget whose state variable is _snapshot_snapshot = AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData);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.