在 Flutter 应用中预加载(所有)图像资源

Posted

技术标签:

【中文标题】在 Flutter 应用中预加载(所有)图像资源【英文标题】:Preload (all) image assets in a Flutter app 【发布时间】:2020-08-04 19:16:44 【问题描述】:

我想要一种简单的方法来预加载/缓存我的所有静态图像资源,以便可以毫不延迟地呈现/提供它们。

我看到有一个precacheImage() 调用可用于预加载/缓存 AssetImage。这需要一个上下文,建议在 didChangeDependencies() 覆盖中调用它。

难道不应该有一种方法可以让这更简单、更通用吗?我的应用程序总共使用了 1.5 MB 的图像数据(我在这个数字中包含了 2.0x 和 3.0x 的升级版本)。 50 KB(并且没有升级版本)的 PNG 图像需要相当长的时间才能显示,在模拟器和快速设备上可能需要 300-600 毫秒。这些是本地资产,不是通过网络获取的。我觉得这很烦人,我很沮丧没有更好的方法来处理这个问题?

我也看到了使用 FadeInImage 的提示,但同样 - 这并不是我真正想要的。

我在无状态小部件(自定义按钮)中显示图像。在无状态小部件 afaik 中使用 precacheImage 是不可能的。所以我需要在我的父小部件中构建 Image.asset(),调用 precacheImage,然后将图像小部件传递给我的无状态小部件并在构建中渲染它 - 这很麻烦。

此外,图像将显示在不同的位置(不同的父小部件)。有时图像小部件在小部件之间的大小不同,因为尺寸是Image.asset() 的参数,我想我需要预先缓存每个唯一大小并传递这些预缓存的图像小部件。是否可以告诉 Flutter “缓存” PNG 的数据,以便在请求 Image.asset 时从缓存中读取 PNG - 而不必传递预缓存的图像小部件?

我想要一个带有字符串的“precacheAllImageAssets()call or callprecacheImage()`,以便缓存引用同一资产的每个 Image.asset()。

我猜 Flutter 在内部缓存了图像小部件(包括它的大小和其他属性)作为缓存的一些内部渲染对象。因此,预缓存同一图像的两个不同大小将需要两个不同的缓存。话虽如此 - 我仍然想要一个 precacheAllImageAssets() 调用,它至少可以将 PNG 数据读入内存并更快地提供它,即使需要进行一些处理以将 PNG 数据获取到具有大小的实际小部件在它被渲染之前。有了这样的缓存,我可能会得到

知道这是否可能吗?如果不可能 - 我是否遗漏了一些明显的东西,或者这可能是 Flutter 框架(可能)未来的改进?

【问题讨论】:

您的问题解决了吗? 您的问题解决了吗? 不,我仍然在特定路径上使用“precacheImage()”缓存特定图像。我仍然想要一种预缓存所有图像的方法。我想我可以迭代根包以获取所有资产路径并根据扩展过滤掉图像,然后预缓存这些,但与我的情况相比,这太过分了。 【参考方案1】:

更新:经过一些研究,我发现我以前的答案版本不正确。这是相关的(你可以在编辑历史中看到旧的)。

您确实需要使用相同的ImageProvider 来预缓存图像。所以你可以在初始化时运行precacheImage(),然后创建另一个具有相同路径的图像,它将从缓存中获取(如果它没有以另一种方式清除)。

在内部precacheImage() 使用ImageProvider.obtainKey() 这是一对(imagePath, scale) 作为将图像存储在内存缓存中的键。因此,只要您使用相同的密钥,您使用的 ImageProvider/Image 的哪个实例都没有关系。

如需进一步了解,您可以查看ImageCache 文档。具体来说,看看putIfAbsent() 方法,因为它是主要的缓存端点。要了解图像如何生成它们的密钥(它们存储在 ImageCache 中),请尝试从 ImageProvider 类开始,然后查看它的实现。

【讨论】:

【参考方案2】:

这里是我类似的precacheAllImageAssets(),但是你需要自己列出所有的图片路径。

final   List _allAsset = [
  ///tabbar
      'images/tabbar/tabar_personal.png',
      'images/tabbar/tabar_personal_slt.png',
      'images/tabbar/tabar_home.png',
      'images/tabbar/tabar_home_slt.png',
      'images/...'
      'images/...'


void main() 
  final binding = WidgetsFlutterBinding.ensureInitialized();

  binding.addPostFrameCallback((_) async 
    BuildContext context = binding.renderViewElement;
    if(context != null)
      
        for(var asset in _allAsset)
        
          precacheImage(AssetImage(asset), context);
        
      
  );

【讨论】:

此代码有效。如果你想预缓存来自网络的图像,请将 AssetImage 替换为 CachedNetworkImageProvider(请注意,你需要使用 pub get 中的 CachedNetworkImage 插件)...但是,代码可以工作...干得好!

以上是关于在 Flutter 应用中预加载(所有)图像资源的主要内容,如果未能解决你的问题,请参考以下文章

如何在 vue 项目中预加载图片

在 SpriteKit 中预加载纹理

Glide:在内存缓存中预加载图像(有或没有磁盘缓存)

使用“预加载”链接指令在 Chrome 中预加载字体

从列表中预加载图像,然后执行回调

在 UIPageViewControllers (Swift) 中预加载所有 UIViewControllers