Flutter网络图片离线缓存加载及ImageProvider异步适配问题
问题核心
errorBuilder要求同步返回Widget,但getAltImage是异步方法,直接调用会返回Future<Widget>而非Widget,不符合参数要求- 原有图片存储/加载代码存在类型错误,会导致读写失败:
saveImage用writeAsString存储二进制Uint8List、loadImage用readAsString读取二进制数据,方法不匹配
第一步:修复底层存储加载代码
先修正存储、加载逻辑的基础错误,保证本地文件读写正常:
// 修复后的图片保存方法 void saveImage(String imageUrl, String imageName) async{ File file = File(await getImagePath(imageName)); ByteData questionMediaData = await NetworkAssetBundle(Uri.parse(imageUrl)).load(""); // 二进制数据用writeAsBytes存储,不要用writeAsString await file.writeAsBytes(questionMediaData.buffer.asUint8List()); } // 修复后的图片加载方法 Future<Uint8List?> loadImage(String mediaName) async{ String path = await getImagePath(mediaName); if(await File(path).exists()){ File file = File(path); // 二进制数据用readAsBytes读取,不要用readAsString return await file.readAsBytes(); } return null; } Future<String> getImagePath(String imageName) async { Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); String appDocumentsPath = appDocumentsDirectory.path; String filePath = '$appDocumentsPath/$imageName'; return filePath; }
第二步:重构OurImageProvider实现
用FutureBuilder包裹异步加载本地图片的逻辑,直接在errorBuilder中返回FutureBuilder组件即可解决异步返回问题:
class OurImageProvider { static Widget getImage(String imageName, {BoxFit fit = BoxFit.cover, double? width, double? height, ImageType imageType = ImageType.standard}) { if(!hasText(imageName)){ if(imageType == ImageType.content) { return Image( image: const AssetImage('assets/dummy_images/catalog_dummy.jpg'), fit: BoxFit.cover, width: width, height: height, ); } else { return const SizedBox.shrink(); } } return Image.network(imageName, fit: fit, width: width, height: height, errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) { // 用FutureBuilder包裹异步加载本地图片的逻辑 return FutureBuilder<Uint8List?>( future: LocalStorage.instance.loadImage(imageName), builder: (context, snapshot) { // 加载完成且存在本地缓存数据 if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) { return Image.memory( snapshot.data!, fit: fit, width: width, height: height, errorBuilder: (context, e, s) => getDummyImage(width, height, fit), ); } // 加载中/加载失败/无本地缓存都返回占位图 return getDummyImage(width, height, fit); }, ); }, ); } static Image getDummyImage(double? width, double? height, BoxFit fit) => Image( image: const AssetImage('assets/dummy_images/dummy.jpg'), width: width, height: height, fit: fit, ); }
可选优化点
- 可在
Image.network加载成功时就调用saveImage存到本地,不用等加载失败才读取缓存,大幅提升离线体验 - 可以给FutureBuilder加统一的加载中占位,避免加载过程中页面空白闪烁
- 可增加本地缓存过期清理逻辑,避免长期使用占用过多存储空间
内容的提问来源于stack exchange,提问作者derChris
相关产品推荐
相关产品推荐

