FutureBuilder 显示加载指示器,尽管结果已缓存

时间:2021-01-31 19:43:35

标签: flutter flutter-provider flutter-futurebuilder

我在尝试将 FutureBuilder 与 Provider 和缓存一起使用时遇到问题。 计算完成一次,然后将其缓存。代码如下所示:

FutureBuilder(
     future: model.calculateStats(chartType: getChartType()),
     builder: (context, snapshot) {
         if(snapshot.connectionState != ConnectionState.done) {
           return Center(
              child: CircularProgressIndicator()
           );
         }

      return buildChart(model);
   },
)

那段代码在一个带有 ViewModel 的 Consumer 中,它的方法是 calculateStats,如下所示:

Future calculateStats({ChartType chartType = ChartType.Monthly}) async {
   return await MemoryCache.instance.getOrCreateAsync("stats:${chartType.index}", () async {
      var statsMaker = StatsMaker(chartType: chartType);
      
      this._currentStats = await statsMaker.generate(
        startDate: _getStartDateForChartType(chartType),
        endDate: DateTime.now()
      );
    }, allowNull: true);
  }

在这里您可以看到正在发生的事情的视频:https://i.imgur.com/SWQ7N7P.mp4

辅助类 MemoryCache 检查提供的键是否在映射中,如果是,则返回值而不进行任何计算,应立即返回,如果未找到,则等待未来并存储结果.但是在这里,虽然结果被缓存了,但 FutureBuilder 会显示加载指示器(视频中为橙色)。我做错了什么?

3 个答案:

答案 0 :(得分:1)

它们可能是 FutureBuilder 继续加载的两个原因。一是因为 calculateStats 不断触发,导致 snapshot 的状态反复刷新。其次是您的 getOrCreateAsync 可能返回一个已经完成的 FutureFutureBuilder 无法同步确定一个 Future 已经完成。

有一种更方便的一次性缓存和加载 UI 的方法,即使用 StreamBuilder,因为您只需在 async 或 { {1}},因此不需要一直重新加载 UI。

您还应该使用 initState 来检查 Future 值是否完整且不为空。 在用户界面中:

didChangeDependencies

在您的 snapshot.hasData 函数中:

@override
initState() {
  super.initState();
  model.calculateStats(chartType: getChartType());
}

// ... other lines

return StreamBuilder(
     stream: model.statsStream,
     builder: (context, snapshot) {
         if(!snapshot.hasData) {
           return Center(
              child: CircularProgressIndicator()
           );
         }

      return buildChart(snapshot.data);
   },
)

确保您的 async 返回非空值,以便正确更新流。

答案 1 :(得分:0)

model.calculateStats(chartType: getChartType()) 是创建未来,还是只是引用未来?如果它创建了一个未来,那么你的 FutureBuilder 将不会完成。您必须在 initState 中创建未来,并将其放在那里的变量中,然后在 future: 处引用该变量。

编辑:啊,现在看,它正在创造一个未来。因此,您需要在 initState 期间将其存储到您的状态中。

答案 2 :(得分:0)

如何检查snapshot.hasData 和snapshot.hasError?