setState对FutureBuilder的使用 [英] Usage of FutureBuilder with setState
问题描述
如何在 setState
中正确使用 FutureBuilder
?例如,当我创建一个有状态的小部件时,它开始加载数据(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.
您必须保持版本
纯净。它应该只读取并组合变量和属性,但绝不会引起任何副作用!
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,
),
);
}
},
);
}
}
这篇关于setState对FutureBuilder的使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!