You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中使用ListView、GridView等构建器加载图片如何避免OOM问题?

Flutter列表仅保留可见项图片资源的解决方案

Flutter 默认的 ImageCache 是全局单例,默认会缓存所有已加载的图片资源,直到占用内存达到预设阈值才会触发LRU清理,这就是列表销毁/项滑出可视区后图片仍占用内存的根本原因。要实现仅保留可见项图片的需求,可参考以下方案:

方案1:列表项销毁时手动清理对应缓存

在列表项的生命周期中精准控制单张图片的缓存释放,不会影响其他页面的缓存逻辑:

  • 加载图片时为每张图片指定唯一的cacheKey,ResizeImage、Image.network均支持该参数,作为缓存清理的唯一标识
  • 在列表项State的dispose方法中调用evict方法,将当前项的图片从全局缓存中移除

示例代码:

class ListImageItem extends StatefulWidget {
  final String imageUrl;
  const ListImageItem({super.key, required this.imageUrl});

  @override
  State<ListImageItem> createState() => _ListImageItemState();
}

class _ListImageItemState extends State<ListImageItem> {
  late String cacheKey;
  late ImageProvider imageProvider;

  @override
  void initState() {
    super.initState();
    // 生成唯一缓存key,建议结合图片地址、缩放尺寸生成避免冲突
    cacheKey = 'img_${widget.imageUrl}_${MediaQuery.of(context).size.width.toInt()}';
    imageProvider = ResizeImage(
      Image.network(widget.imageUrl).image,
      width: MediaQuery.of(context).size.width.toInt(),
      cacheKey: cacheKey,
    );
  }

  @override
  void dispose() {
    // 列表项销毁时清理当前图片缓存
    PaintingBinding.instance.imageCache.evict(cacheKey);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Image(image: imageProvider, fit: BoxFit.cover);
  }
}

方案2:禁用单张图片的全局缓存

如果不需要跨页面/跨列表复用同一张图片,可以直接跳过全局缓存逻辑,完全由列表项自身持有图片资源,列表项销毁时资源会自动被GC回收:

  • 自定义ImageProvider实现加载逻辑,跳过向全局ImageCache存取数据的步骤
  • 对于Image.memory场景,确保没有其他全局变量持有传入的Uint8List引用即可,列表项销毁后对应内存会自动释放

方案3:维护列表局部缓存

如果需要保证短距离滑动的流畅度(避免刚滑过的图片再次滑入时重复加载),可以给当前列表单独维护一个缓存池,缓存容量设置为可见项数量的1.5~2倍,超出容量的旧资源自动清理,全局缓存则禁用当前列表的图片存储,兼顾性能和内存占用控制。

注意事项

  • 手动清理缓存前确认对应图片没有其他页面/组件在使用,避免不必要的重复加载
  • cacheKey的生成规则要和加载图片时的规则完全一致,否则会出现清理失效的问题
  • 不要直接清空全局ImageCache,会影响其他页面的正常缓存逻辑

内容的提问来源于stack exchange,提问作者Tom3652

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 11:45:03