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

Posted

技术标签:

【中文标题】FutureBuilder 显示加载指示器,尽管结果已缓存【英文标题】:FutureBuilder shows the loading indicator althought the result is cached 【发布时间】:2021-05-05 02:01:45 【问题描述】:

我在尝试将 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 显示了加载指示器(在视频中是橙色的)。我做错了什么?

【问题讨论】:

【参考方案1】:

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

有一种更方便的方式来一次性缓存和加载 UI,即使用 StreamBuilder,因为您只需在 initStatedidChangeDependencies 中调用一次 async 方法,因此 UI不需要一直重新加载。

您还应该使用snapshot.hasData 来检查Future 值是否已完成且不为空。 在用户界面中:

@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 函数中:

StreamController _controller = StreamController();

Stream get statStream => _controller.stream;

Future calculateStats(ChartType chartType = ChartType.Monthly) async 
   final stats = await MemoryCache.instance.getOrCreateAsync( ... );
   // add your stats to Stream here (stats is the value you want to send to UI)
   _controller.add(stats);

确保您的 getOrCreateAsync 返回一个非空值,以便正确更新 Stream。

【讨论】:

这是一个很好的解决方案,谢谢。我认为这是 BloC 处理更新的方式,对吧? 是的,Stream 仅在有新数据发送到其中时才会收到通知【参考方案2】:

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

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

【讨论】:

我在我的新视频中说明了这一点:youtube.com/watch?v=sqE-J8YJnpg【参考方案3】:

检查snapshot.hasData和snapshot.hasError怎么样?

【讨论】:

以上是关于FutureBuilder 显示加载指示器,尽管结果已缓存的主要内容,如果未能解决你的问题,请参考以下文章

未来列表和刷新加载器|颤动的问题

Flutter get_it 和 FutureBuilder 无法使用热重载

FLutter入门:异步加载组件FutureBuilder

使用具有 FutureBuilder 作为子级的 StatefulWidget List 显示没有数据,直到我热重新加载然后立即调用没有数据的未来

每当更改 BottomNavigationBarItem 时,FutureBuilder 都会重新加载

为啥futureBuilder有时会卡在进度加载器上?