Flutter如何防止FutureBuilder重复调用异步请求函数
问题根源
你遇到的重复调用问题核心原因是:每次页面触发重绘(比如滚动列表、状态更新),build方法执行时ListView的itemBuilder都会重新执行一遍_imageFuture = getimage(carUserId),相当于每次都生成新的Future实例,FutureBuilder检测到future参数变更就会重新执行请求逻辑。
你之前在父页面initState调用不生效,是因为此时cars列表数据还没加载完成,拿不到每个列表项对应的carUserId,且不同列表项的carUserId各不相同,没法在父页面一次性初始化所有请求。
解决方案
方案1:新增缓存Map存储已请求的内容
在当前页面的State类中新增一个缓存Map,已经请求过的用户头像直接复用之前的Future,避免重复发起请求:
// 定义在State类中,不要放在build方法里 Map<String, Future<String>> _userImageCache = {}; // 如果getNumber也需要按用户区分请求,也可以加对应的缓存 Map<String, Future> _userNumberCache = {};
然后修改itemBuilder里的逻辑,先查缓存再决定是否发起新请求:
itemBuilder: (context, index) { carUserId = (cars!.docs[index].data() as Map)['uId']; // 头像请求先查缓存 if (!_userImageCache.containsKey(carUserId)) { _userImageCache[carUserId] = getimage(carUserId); } _imageFuture = _userImageCache[carUserId]!; // getNumber如果也要去重同理 if (!_userNumberCache.containsKey(carUserId)) { _userNumberCache[carUserId] = getNumber(); } _mFuture = _userNumberCache[carUserId]!; // 其余逻辑不变 return Card(...) }
这种方案改动最小,同一个用户的头像和手机号只会请求一次,哪怕列表反复滚动重绘也不会重复调用接口。
方案2:把列表项抽成独立的StatefulWidget
将每个列表项的Card封装成独立的StatefulWidget,在子组件的initState中发起请求,每个列表项只会在第一次创建的时候执行一次请求,后续重绘不会重复触发:
// 单独封装的列表项组件 class CarListItem extends StatefulWidget { final Map carItemData; final String currentLoginUserId; const CarListItem({ super.key, required this.carItemData, required this.currentLoginUserId }); @override State<CarListItem> createState() => _CarListItemState(); } class _CarListItemState extends State<CarListItem> { late Future<String> _imageFuture; late Future _numberFuture; @override void initState() { super.initState(); // 仅在子组件初始化时执行一次请求 _imageFuture = getimage(widget.carItemData['uId']); _numberFuture = getNumber(); } @override Widget build(BuildContext context) { // 直接复用初始化好的Future,原来的Card布局逻辑直接搬过来即可 // 把之前所有的cars!.docs[index].data()替换成widget.carItemData // 把之前的userId替换成widget.currentLoginUserId return Card( // 原有布局内容 ); } }
修改原ListView的itemBuilder:
itemBuilder: (context, index) { var carData = cars!.docs[index].data() as Map; return CarListItem( carItemData: carData, currentLoginUserId: userId ); }
这种方案代码结构更清晰,后续维护列表项逻辑也更方便。
优化建议
你代码里的图片加载可以替换成cached_network_image库,实现图片的本地缓存,下次打开页面不用重复请求网络,加载速度更快,也能避免重复的图片网络请求。
内容的提问来源于stack exchange,提问作者sepand mehraein
相关产品推荐
相关产品推荐

