Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usage of FutureBuilder with setState

Tags:

flutter

dart

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,                 ),               );         }       },     );   } } 
like image 721
Артем Логанов Avatar asked Aug 25 '18 21:08

Артем Логанов


People also ask

When should I use a FutureBuilder?

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.

How do you use future builder?

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.

Does setState call build?

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.


2 Answers

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,             ),           );         }       },     );   } } 
like image 130
boformer Avatar answered Oct 13 '22 23:10

boformer


Use can SchedulerBinding for using setState() inside Future Builders or Stream Builder,

 SchedulerBinding.instance                 .addPostFrameCallback((_) => setState(() {               isServiceError = false;               isDataFetched = true;             })); 
like image 36
Navin Kumar Avatar answered Oct 13 '22 23:10

Navin Kumar