When to use FutureBuilder in Flutter
Solution 1
FutureBuilder
is used to handle "asynchronous" calls that return the data you want to display.
For example, let's say we have to get a List<String>
from the server.
The API call :
Future<List<String>> getStringList() async {
try {
return Future.delayed(Duration(seconds: 1)).then(
(value) => ['data1', 'data2', 'data3', 'data4'],
);
} catch (e) {
throw Exception(e);
}
}
How can we handle the above API call status (load, data, error...etc) using FutureBuilder
:
FutureBuilder<List<String>?>(
future: getStringList(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
if (snapshot.hasError)
return Text(snapshot.error.toString());
else
return ListView(
children: snapshot.data!.map((e) => Text(e)).toList(),
);
default:
return Text('Unhandle State');
}
},
),
As we can see, we don't have to create a state class with variable isLoading
bool
and String
for error...etc. FutureBuilder
saves us time.
BUT
Since FutureBuilder
is a widget and lives in the widget tree, rebuilding can make FutureBuilder
run again.
So I would say you can use FutureBuilder
when you know there won't be a rebuild, however there are many ways (in the internet) to prevent FutureBuilder
from being called again when the rebuild happens but it didn't work for me and leads to unexpected behavior.
Honestly I prefer handling the state in a different class with any state management solution than using FutureBuilder
because it would be safer (rebuild wont effect it), more usable and easier to read (spreating business logic from UI).
Solution 2
Actually, you will never need to use FutureBuilder Widget if you don't want to. Your logic in your code do exactly what FutureBuilder Widget does if you optimise FutureBuilder Widget correctly.
This code is exactly same with yours:
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool done = false;
late Future myFuture;
@override
void initState() {
myFuture = wait();
super.initState();
}
Future<bool> wait() async {
await Future.delayed(const Duration(seconds: 2));
return true;
}
@override
Widget build(BuildContext context) {
print('is built');
return FutureBuilder(
future: myFuture,
builder: (BuildContext context, snapshot) {
if(snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(body: CircularProgressIndicator(),);
} else {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
);
}
}
Solution 3
FutureBuilder
Widget that builds itself based on the latest snapshot of interaction with a Future.
The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder.
If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.A general guideline is to assume that every build method could get called every frame, and to treat omitted calls as an optimization.
Documentation is very great way to get started and understand what widget does what in what condition...
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
Comments
-
user14624595 10 months
I would like to know when to use a FutureBuilder and how is it usefull, given the fact that a widget can be built multiple times during its life time and not only when we setState or update it, so instead of using below code:
class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { bool done = false; @override void initState() { wait(); super.initState(); } Future<void> wait() async { await Future.delayed(Duration(seconds: 2)); setState(() { done = true; }); } @override Widget build(BuildContext context) { print('is built'); return done ? Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Text( '', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () {}, tooltip: 'Increment', child: Icon(Icons.add), ), ) : Scaffold(body: CircularProgressIndicator(),); } }
In which cases would a FutureBuilder work for me instead of the above set up, given the fact that I would also want to optimize my app for less backend reads (in FutureBuilder I would read more than once). I am looking for a case where FutureBuilder would be more usefull and correct than the above setup.
-
user14624595 over 2 yearsThis doesn't really answer my question. I am looking for a case where I would need to use FutureBuilder instead of the setup above.
-
Aman khan Roohaani over 2 years@user14624595 please update your question with a proper context. make the question more clear.
-
user14624595 about 2 yearsSure there is impact , there are more rebuilds and api calls into the backend. If for example we used firestore , then we make unnecessary reads, or give more traffic to our backend for no reason. If for example user resizes his window on flutter web, build will be called again without a need, since we already have the data with init state method.
-
user14624595 about 2 yearsso basically if you want to write the best possible code in terms of performance and api calls, futurebuilder is useless, or more like a bad practice. I cant think of a case where the above setup would not work and I would need to use FutureBuilder instead.
-
Mohammed Alfateh about 2 yearsYes, the out-of-the-box solution can come with downsides.
-
user14624595 about 2 yearsI picked your answer as a correct one, since it was the most helpfull
-
user14624595 about 2 yearsYou can refactor the widget where you want the future data, into a different statefull widget and implement initState method there, that way you also rebuild a part of the tree, so no difference there.
-
Arul about 2 yearsStateful widget is heavy for update single child and when the application becomes larger we should avoid use much Stateful Widget and consume Future or GetX or any other method.
-
user14624595 about 2 yearsdon't those approaches also call set state under the hood? Do you have a link to what you claim?
-
Arul about 2 yearsOf course but it does not affect the full widget tree, its updates a particular widget. And in the case of use FutureBuilder, we shouldn't use setState instead we return the future to the futurebuilder it takes care of the update tree.
-
user14624595 about 2 yearsyes, but since it is in the build function it can be called any number of times, while initState is called only once when the page/widget starts, so only the part of the particular widget will change and not rebuild the entire widget tree