How to use the FutureBuilder
with setState
properly? For example, when i create a stateful widget its starting to load data (FutureBuilder) and then i should update the list with new data, so i use setState, but its starting to loop for infinity (because i rebuild the widget again), any solutions?
class FeedListState extends State<FeedList> { Future<Null> updateList() async { await widget.feeds.update(); setState(() { widget.items = widget.feeds.getList(); }); //widget.items = widget.feeds.getList(); } @override Widget build(BuildContext context) { return new FutureBuilder<Null>( future: updateList(), builder: (BuildContext context, AsyncSnapshot<String> snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return new Center( child: new CircularProgressIndicator(), ); default: if (snapshot.hasError) return new Text('Error: ${snapshot.error}'); else return new Scrollbar( child: new RefreshIndicator( child: ListView.builder( physics: const AlwaysScrollableScrollPhysics(), //Even if zero elements to update scroll itemCount: widget.items.length, itemBuilder: (context, index) { return FeedListItem(widget.items[index]); }, ), onRefresh: updateList, ), ); } }, ); } }
In Flutter, the FutureBuilder Widget is used to create widgets based on the latest snapshot of interaction with a Future. It is necessary for Future to be obtained earlier either through a change of state or change in dependencies.
Show a constructor and parameters of Streambuilder. That is the way to utilize FutureBuilder in Flutter. You need to make a Future and pass it as the future argument. The snapshots of the Future will be passed to the builder function, in which you can decide the format to be shown depending on the current snapshot.
Calling setState notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a build for this State object.
Indeed, it will loop into infinity because whenever build
is called, updateList
is also called and returns a brand new future.
You have to keep your build
pure. It should just read and combine variables and properties, but never cause any side effects!
Another note: All fields of your StatefulWidget
subclass must be final (widget.items = ...
is bad). The state that changes must be stored in the State
object.
In this case you can store the result (the data for the list) in the future itself, there is no need for a separate field. It's even dangerous to call setState
from a future, because the future might complete after the disposal of the state, and it will throw an error.
Here is some update code that takes into account all of these things:
class FeedListState extends State<FeedList> { // no idea how you named your data class... Future<List<ItemData>> _listFuture; @override void initState() { super.initState(); // initial load _listFuture = updateAndGetList(); } void refreshList() { // reload setState(() { _listFuture = updateAndGetList(); }); } Future<List<ItemData>> updateAndGetList() async { await widget.feeds.update(); // return the list here return widget.feeds.getList(); } @override Widget build(BuildContext context) { return new FutureBuilder<List<ItemData>>( future: _listFuture, builder: (BuildContext context, AsyncSnapshot<List<ItemData>> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return new Center( child: new CircularProgressIndicator(), ); } else if (snapshot.hasError) { return new Text('Error: ${snapshot.error}'); } else { final items = snapshot.data ?? <ItemData>[]; // handle the case that data is null return new Scrollbar( child: new RefreshIndicator( child: ListView.builder( physics: const AlwaysScrollableScrollPhysics(), //Even if zero elements to update scroll itemCount: items.length, itemBuilder: (context, index) { return FeedListItem(items[index]); }, ), onRefresh: refreshList, ), ); } }, ); } }
Use can SchedulerBinding for using setState() inside Future Builders or Stream Builder,
SchedulerBinding.instance .addPostFrameCallback((_) => setState(() { isServiceError = false; isDataFetched = true; }));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With