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 isLoadingFlag
isLoadingFlag = 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
setState
FutureBuilder
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.