如何防止FutureBuilder重建,即使其数据未更改?

时间:2019-09-18 23:08:23

标签: api flutter dart

我目前正在开发Flutter应用程序,该应用程序使用许多REST API与服务器端进行通信。我面临一些与FutureBuilder小部件相关的性能问题,具体实现如下:

RefreshIndicator(
  displacement: 100.0,
  onRefresh: _refreshLastEntries,
  child: FutureBuilder<List<Entry>> (
    future: lastEntriesList,
    builder: (context, snapshot) {
      if(snapshot.hasError) print(snapshot.error);
      if(snapshot.data == null) return Container();
      else return ListView.builder(
        controller: _controller,
        itemCount: snapshot.data.length,
        itemBuilder: (context, index) {
          return Padding(
            padding: const EdgeInsets.all(10.0),
            child: EntryCard(entry: snapshot.data[index])
          );
        },
      );
    },
  ),
),

顺便说一句,在同一页面构建中,还有2个带有RefreshIndicator的FutureBuilder实现,它们都由PageView包裹。 Android Studio中的Flutter Performance选项卡显示平均值。在调试模式下为30帧/秒(有时<30)的fps。在发布模式下效果更好,但仍低于60 fps,我可以注意到动画效果不佳。

我知道问题(虽然不确定)是FutureBuilder尝试重建,只要AsyncSnapshot有数据。但是我想不出更好的实现方法。刷新功能_refreshLastEntries()实现为:

Future<Null> _refreshLastEntries() async {
  setState(() {
     lastEntriesList = fetchLastEntries(2);
  });
  return Future.delayed(Duration(milliseconds: 600));
}

fetchLastEntries(int)仅用于获取数据并将其映射到某些EntryModel类中。 成功获取数据后,我尝试设置一个全局值,然后将其用于构建ListView,但是它不起作用。

如果有问题,我正在使用配备Snapdragon 835和Adreno 540的Xiaomi MI6测试此应用程序。

由于很难复制相同的版本,因此我不会将任何其他代码附加为整个PageView构建代码,谢谢!

1 个答案:

答案 0 :(得分:1)

如果将来的状态不变,则FutureBuilder小部件不应重建。您的情况可能是

setState(() {
   lastEntriesList = fetchLastEntries(2);
});

内部_refreshLastEntries()导致未完成的将来进行重建,完成后将通过FutureBuilder进行另一次重建。

由于您使用的是RefreshIndicator,因此我认为您不需要FutureBuilder。在_refreshLastEntries()内部进行异步工作,完成后,更新ListView.builder小部件。要更清楚:

//Declare lastEntriesList as a list
List<Entry> lastEntriesList;

Future<void> _refreshLastEntries() async {
  return fetchLastEntries(2).then((lastEntries) {
    setState(() {
      lastEntriesList = lastEntries;
    });
  });
}

并如下修改您的子树:

RefreshIndicator(
  displacement: 100.0,
  onRefresh: _refreshLastEntries,
  child: ListView.builder(
    controller: _controller,
    itemCount: lastEntriesList.length,
    itemBuilder: (context, index) {
      return Padding(
        padding: const EdgeInsets.all(10.0),
        child: EntryCard(entry: lastEntrieslist[index])
      );
    },
  );,
)