FutureBuilder 显示加载指示器,尽管结果已缓存
FutureBuilder shows the loading indicator althought the result is cached
我在尝试将 FutureBuilder 与提供程序和缓存一起使用时遇到问题。
计算完成一次,然后缓存。代码如下所示:
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
助手 class MemoryCache 检查提供的键是否在映射中,如果是,它 return 不做任何计算就得到值,这应该 return立即,如果没有找到,则等待未来并存储结果。但是在这里,虽然结果被缓存了,但 FutureBuilder 显示了加载指示器(在视频中是橙色的)。我做错了什么?
model.calculateStats(chartType: getChartType())
是创造未来,还是只是参考未来?如果它创建了一个未来,那么你的 FutureBuilder 将不会完成。您必须在 initState 中创建未来,并将其放入一个变量中,然后在 future:
.
处引用该变量
编辑:啊,现在看,是在创造未来。因此,您需要在 initState 期间将其存储到您的状态中。
检查 snapshot.hasData 和 snapshot.hasError 怎么样?
他们可能有 2 个原因 FutureBuilder
继续加载。一是因为calculateStats
一直在触发,导致snapshot
的状态反复刷新。其次是您的 getOrCreateAsync
可能 return 已经完成 Future
,FutureBuilder
无法同步确定 Future 已经完成。
有一种更方便的一次性缓存和加载 UI 的方法,即使用 StreamBuilder
,因为您只需要在 initState
或 didChangeDependencies
,因此 UI 不需要一直重新加载。
您还应该使用 snapshot.hasData
来检查 Future 值是否已完成且不为空。
在 UI:
@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
return 为非空值,以便正确更新流。
我在尝试将 FutureBuilder 与提供程序和缓存一起使用时遇到问题。 计算完成一次,然后缓存。代码如下所示:
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
助手 class MemoryCache 检查提供的键是否在映射中,如果是,它 return 不做任何计算就得到值,这应该 return立即,如果没有找到,则等待未来并存储结果。但是在这里,虽然结果被缓存了,但 FutureBuilder 显示了加载指示器(在视频中是橙色的)。我做错了什么?
model.calculateStats(chartType: getChartType())
是创造未来,还是只是参考未来?如果它创建了一个未来,那么你的 FutureBuilder 将不会完成。您必须在 initState 中创建未来,并将其放入一个变量中,然后在 future:
.
编辑:啊,现在看,是在创造未来。因此,您需要在 initState 期间将其存储到您的状态中。
检查 snapshot.hasData 和 snapshot.hasError 怎么样?
他们可能有 2 个原因 FutureBuilder
继续加载。一是因为calculateStats
一直在触发,导致snapshot
的状态反复刷新。其次是您的 getOrCreateAsync
可能 return 已经完成 Future
,FutureBuilder
无法同步确定 Future 已经完成。
有一种更方便的一次性缓存和加载 UI 的方法,即使用 StreamBuilder
,因为您只需要在 initState
或 didChangeDependencies
,因此 UI 不需要一直重新加载。
您还应该使用 snapshot.hasData
来检查 Future 值是否已完成且不为空。
在 UI:
@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
return 为非空值,以便正确更新流。