如何避免图片下载后重复发起URL请求?Flutter代码问题求助
Flutter图片加载:解决重复请求与显示异常问题
问题场景
你尝试用alreadyDdl变量避免重复发起HTTP请求,但出现两个问题:
- 图片始终无法显示,只能看到占位图
assets/none.png - 移除
alreadyDdl后,代码每15毫秒发起一次重复请求
原核心代码:
var alreadyDdl = false; getLogoUrl(context) async { if(!alreadyDdl) { final db = Localstore.instance; final data = db.collection('inputs').doc("1").get(); var database = (await data)["content"].toString(); var form = new DGForm("project/getwebsitelogo", {"database": database}); var ret = await form.urlGET(context); ResponseObject responseObject = ResponseObject.fromJson(json.decode(ret.body)); var hola = (responseObject.datas[0][0].toString()); bandeauDuClient = hola; return hola; } } getLogoUrl(context).then((val) { setState(() => logoUrl = val ); alreadyDdl = true; });
图片显示判断代码:
(logoUrl != null) ? Image.network(logoUrl): Image.asset('assets/none.png')
问题根源
alreadyDdl时机错误:你在请求完成后才设置alreadyDdl = true,但如果getLogoUrl因alreadyDdl为true直接返回null,会触发then回调把logoUrl设为null,导致占位图一直显示。- build方法重复触发请求:如果
getLogoUrl的调用放在build方法里,每次setState都会重新执行build,进而重复调用getLogoUrl,即使alreadyDdl为true,也会返回null覆盖已有的logoUrl。
解决方案
方案1:将请求移至initState(推荐)
initState仅在Widget初始化时执行一次,从根源避免重复请求:
class _YourWidgetState extends State<YourWidget> { String? logoUrl; @override void initState() { super.initState(); _fetchLogo(); } Future<void> _fetchLogo() async { try { // 读取本地数据 final db = Localstore.instance; final data = await db.collection('inputs').doc("1").get(); final database = data?["content"].toString(); if (database == null) return; // 发起网络请求 final form = DGForm("project/getwebsitelogo", {"database": database}); final ret = await form.urlGET(context); final responseObject = ResponseObject.fromJson(json.decode(ret.body)); final logoUrl = responseObject.datas[0][0].toString(); // 更新UI setState(() { this.logoUrl = logoUrl; }); } catch (e) { print("加载Logo失败: $e"); } } @override Widget build(BuildContext context) { return logoUrl != null ? Image.network(logoUrl!) : Image.asset('assets/none.png'); } }
方案2:修正状态逻辑+使用FutureBuilder
如果必须在build中处理请求,用FutureBuilder管理异步状态,同时修正请求标记的设置时机:
class _YourWidgetState extends State<YourWidget> { bool _isLoading = false; String? _cachedLogoUrl; Future<String?> _getLogoUrl(BuildContext context) async { if (_cachedLogoUrl != null) return _cachedLogoUrl; if (_isLoading) return null; // 防止并发请求 _isLoading = true; try { final db = Localstore.instance; final data = await db.collection('inputs').doc("1").get(); final database = data?["content"].toString(); if (database == null) return null; final form = DGForm("project/getwebsitelogo", {"database": database}); final ret = await form.urlGET(context); final responseObject = ResponseObject.fromJson(json.decode(ret.body)); final logoUrl = responseObject.datas[0][0].toString(); _cachedLogoUrl = logoUrl; return logoUrl; } catch (e) { print("请求失败: $e"); return null; } finally { _isLoading = false; } } @override Widget build(BuildContext context) { return FutureBuilder<String?>( future: _getLogoUrl(context), initialData: _cachedLogoUrl, builder: (context, snapshot) { if (snapshot.hasData && snapshot.data != null) { return Image.network(snapshot.data!); } else { return Image.asset('assets/none.png'); } }, ); } }
关键优化点
- 把请求标记(
_isLoading)放在请求开始前,避免并发重复请求 - 缓存已成功获取的
logoUrl,后续直接返回缓存值 - 使用
initState或FutureBuilder管理异步请求,避免在build中直接触发请求导致的重复执行
内容的提问来源于stack exchange,提问作者lessmore
相关产品推荐
相关产品推荐

