如何保存FutureProvider数据,避免反复重建引发API请求次数超限?
问题根源
你将FutureProvider放在ListView.builder的itemBuilder回调内,每次条目滚出屏幕再滚回时,条目会重新构建,create回调就会重新执行触发新的API请求。你仅开启了ListView的addAutomaticKeepAlives配置,但条目本身没有实现保活逻辑,且每次构建都会生成新的FutureProvider实例,原有状态直接丢失,请求自然重复触发。
修复方案
下面按优先级给可落地的修复方案:
方案1:新增请求缓存层(最推荐,性能最优)
不要在条目构建时触发请求,提前在页面上层统一管理所有请求的缓存,同一个index的请求仅触发一次:
// 首先在当前页面的State中定义缓存容器 Map<int, List<dynamic>> _fireIceCountCache = {}; // 封装带缓存的请求方法 Future<List<dynamic>> _getCachedFireIceCount(int index, List places) async { if (_fireIceCountCache.containsKey(index)) { return _fireIceCountCache[index]!; } final result = await getFireIceCount(index, places); _fireIceCountCache[index] = result; return result; }
之后把itemBuilder里create回调的调用替换成_getCachedFireIceCount(index, places)即可,就算条目重建,同一个index的请求只会执行一次。
方案2:实现条目保活
如果你的条目数量不多(少于20个),可以直接保活所有条目,避免重建:
- 把条目逻辑单独抽成一个StatefulWidget
- 给Widget的State混入
AutomaticKeepAliveClientMixin,配置wantKeepAlive返回true
示例代码:
class PlaceItem extends StatefulWidget { final int index; final List places; const PlaceItem({super.key, required this.index, required this.places}); @override State<PlaceItem> createState() => _PlaceItemState(); } class _PlaceItemState extends State<PlaceItem> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); // 必须调用 return FutureProvider<List<dynamic>?>( initialData: null, create: (context) => getFireIceCount(widget.index, widget.places), child: Consumer<List<dynamic>?>( builder: (context, fireIceCount, child) { if (fireIceCount == null) { return const Center(child: CircularProgressIndicator(color: Colors.red)); } if (fireIceCount.isNotEmpty) { return PlacesImageViewer(); } return const Center(child: CircularProgressIndicator(color: Colors.cyan)); }, ), ); } }
之后ListView的itemBuilder直接返回PlaceItem(index: index, places: places)即可。
方案3:预创建Future缓存
在页面初始化阶段就把所有index对应的Future提前创建好存在List中,itemBuilder的create回调直接返回对应index的Future,同一个Future只会执行一次:
// 在State中定义Future缓存列表 late List<Future<List<dynamic>>> _fireIceCountFutures; @override void initState() { super.initState(); // 页面初始化时一次性创建所有Future _fireIceCountFutures = List.generate(places.length, (index) => getFireIceCount(index, places)); }
之后itemBuilder里create回调返回_fireIceCountFutures[index]即可。
内容的提问来源于stack exchange,提问作者Arslan Kaleem
相关产品推荐
相关产品推荐

