Flutter本地图片缓存

265 阅读2分钟

Flutter本地图片缓存

缓存工具类源码

解决的问题是:加载本地图片,但是依旧会卡顿一下显示的问题。在项目的启动阶段调用(主要是在显示图片之前的那个界面调用)。

调用与源码

image-20250923104800315.png

import 'package:flutter/widgets.dart';
import 'package:myapp/app/utils/logger/logger.dart';
import 'package:myapp/gen/assets.gen.dart';

class LocalImageCache {
  /// 缓存 Flutter Gen 自动生成的所有图片
  static Future<void> precacheAll(BuildContext context) async {
    // 所有需要缓存的图片路径
    final images = <AssetGenImage>[
      Assets.images.login.headBgLg,
      Assets.images.question.headBackground,
      Assets.images.booking.rightJiantou,
      Assets.images.booking.yuzuHead,
      Assets.images.booking.paizhao,
      Assets.images.yuding.headWoshou,
      Assets.images.question.headBackground,
      Assets.images.login.loginPhoneWhite,
      Assets.images.login.cryIconLg,
      Assets.images.xgmm.mail,
      Assets.images.xgmm.suo,
      Assets.images.xgmm.cha,
      Assets.images.xgmm.mail,
      Assets.images.xgmm.cha,
      Assets.images.people.banIcon,
      Assets.images.people.yuzuIcon,
      Assets.images.xingge.xingge,
      Assets.images.pingjia.juziA,
      Assets.images.pingjia.juziB,
      Assets.images.pingjia.juziC,
      Assets.images.pingjia.juziD,
      Assets.images.pingjia.juziE,
      Assets.images.pingjia.juziF,
      Assets.images.pingjia.juziWoshou,
      Assets.images.pingjia.message,
      Assets.images.pingjia.juziList,
      Assets.images.pingjia.juziKong,
      Assets.images.pingjia.juzi,
      Assets.images.home.homeDark,
      Assets.images.home.homeLight,
      Assets.images.home.messageDark,
      Assets.images.home.messageLight,
      Assets.images.home.peopleDark,
      Assets.images.home.peopleLight,
    ];
    for (final img in images) {
      await precacheImage(img.image().image, context);
    }
    logger.d("✅ 所有本地图片预缓存完成");
  }

  /// 清除所有图片缓存(内存缓存 + 未使用缓存)
  static void clearAll() {
    final cache = PaintingBinding.instance.imageCache;
    cache.clear(); // 清空所有缓存
    cache.clearLiveImages(); // 再清空未使用的缓存
    logger.d("🗑️ 所有本地图片缓存已清除");
  }

  /// 清除指定图片缓存
  static void clearOne(AssetGenImage img) {
    final provider = img.image().image;
    PaintingBinding.instance.imageCache.evict(provider);
    logger.d("🗑️ 图片缓存已清除: ${img.path}");
  }
}

能命中缓存 的调用方式例子(假设你已经在 LocalImageCache.precacheAll(context) 里缓存了 Assets.images.home.homeDark):


✅ 会命中缓存的几种写法

方式一:直接用 AssetGenImage.image()
Assets.images.home.homeDark.image(
  width: 24,
  height: 24,
);

这是最标准的,和预缓存时的 img.image().image 一致。


方式二:用 Image.asset,路径相同
Image.asset(
  'assets/images/home/home_dark.png',
  width: 24,
  height: 24,
);

生成的 AssetImage('assets/images/home/home_dark.png') 和预缓存的 AssetImage 一致,所以能命中缓存。


方式三:手动创建同一个 AssetImage
const AssetImage assetImg = AssetImage('assets/images/home/home_dark.png');

Image(
  image: assetImg,
  width: 24,
  height: 24,
);

这里 assetImgAssets.images.home.homeDark.image().image 是相同的 AssetImage,所以也能用缓存。


⚠️ 不会命中缓存的几种情况

  1. 解码参数不一样

    // 预缓存时用 24x24
    Assets.images.home.homeDark.image(width: 24, height: 24);
    
    // 使用时换成 48x48
    Assets.images.home.homeDark.image(width: 48, height: 48);
    

    Flutter 会认为这是不同的 ImageProvider,重新解码。

  2. 不同 bundle

    Image.asset(
      'assets/images/home/home_dark.png',
      bundle: rootBundle, // 和默认 bundle 不一样
    );
    

    bundle 不同,无法命中。

  3. 缓存被清理 如果缓存数量或大小超过 PaintingBinding.instance.imageCache 的限制,旧图片会被回收,再次使用会重新解码。


👉 总结一下:

  • 只要路径和 bundle 一致,就能命中缓存。
  • 大小(width/height)不同会导致不一致。
  • 最推荐的方式就是始终用 Assets.images.xxx.image() 保持一致。