FutureBuilder
を適切に使用するにはどうすればよいでしょうかsetState
? たとえば、ステートフル ウィジェットを作成すると、データの読み込みが開始され (FutureBuilder)、新しいデータでリストを更新する必要があるため、setState を使用しますが、無限ループが始まります (ウィジェットを再度再構築するため)。解決策はありますか?
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,
),
);
}
},
);
}
}
ベストアンサー1
build
実際、が呼び出されるたびに、updateList
これも呼び出され、まったく新しい future を返すため、無限ループが発生します。
純粋さを維持する必要がありますbuild
。変数とプロパティを読み取って組み合わせるだけで、副作用が発生することはありません。
もう一つの注意:StatefulWidget
サブクラスのすべてのフィールドは final である必要があります (widget.items = ...
は良くありません)。変更された状態はオブジェクトに保存される必要がありますState
。
この場合、結果 (リストのデータ) を future 自体に保存することができ、別のフィールドは必要ありません。futuresetState
から呼び出すのは危険です。future は状態の破棄後に完了する可能性があり、エラーがスローされるからです。
これらすべてのことを考慮した更新コードを次に示します。
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,
),
);
}
},
);
}
}