FutureBuilder 与 setState 的使用 [英] Usage of FutureBuilder with setState
问题描述
如何正确使用FutureBuilder
和setState
?例如,当我创建一个有状态小部件时,它开始加载数据(FutureBuilder),然后我应该用新数据更新列表,所以我使用 setState,但它开始无限循环(因为我再次重建小部件),任何解决方案?
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,
),
);
}
},
);
}
}
推荐答案
确实,它会无限循环,因为每当 build
被调用时,updateList
也会被调用并返回一个全新的未来.
Indeed, it will loop into infinity because whenever build
is called, updateList
is also called and returns a brand new future.
你必须保持你的 build
纯净.它应该只是读取和组合变量和属性,但绝不会造成任何副作用!
You have to keep your build
pure. It should just read and combine variables and properties, but never cause any side effects!
另一个注意事项:StatefulWidget
子类的所有字段都必须是最终的(widget.items = ...
是错误的).更改的状态必须存储在 State
对象中.
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.
在这种情况下,您可以将结果(列表的数据)存储在未来本身中,不需要单独的字段.从未来调用 setState
甚至是危险的,因为未来可能会在处理状态后完成,并且会抛出错误.
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,
),
);
}
},
);
}
}
这篇关于FutureBuilder 与 setState 的使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!