在 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 call
precacheImage()`,以便缓存引用同一资产的每个 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 应用中预加载(所有)图像资源的主要内容,如果未能解决你的问题,请参考以下文章