setState での FutureBuilder の使用 質問する

setState での FutureBuilder の使用 質問する

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,
            ),
          );
        }
      },
    );
  }
}

おすすめ記事