手记

Flutter中处理图片时常见的坑及优化方法

图片是几乎每个应用程序都会使用的核心功能。然而,由于一些简单的错误,有几种常见的方法会影响应用性能。下面我们来看一些常见的错误。

1 大图资源

这会占用大量的内存和处理资源。解码后的位图大小直接与解码分辨率相关,这将显著影响应用程序的运行效率。

简化捆绑图像文件

大多数智能手机的屏幕宽度不超过1200像素,因此相应地调整资源文件大小是有意义的。我们以一张7500x5000像素的照片为例。存储这样大小的位图需要112MB的内存,这超出了Flutter中的默认图片缓存大小。一个简单的经验法则是,在一次应用会话中,位图的总大小不应超过100MB。否则,图片将被重新解码,这会使用户体验变得不那么顺畅。

将图像分辨率调整为1200x800后,位图大小降至2.8兆字节。可以使用this tool来进行计算。

然而,我们只有在掌控这些文件时才能做到这一点,但如果图片是从远程来源来的,我们又该怎么办?或者同一张图片可以在不同布局中以不同尺寸使用,这种情况下我们又该如何处理?

设置缓存宽度和高度

通过提供这些参数,我们可以指定图像的解码尺寸。别忘了在计算中加入 MediaQuery.of(context).devicePixelRatio。另外,具有不同 cacheHeightcacheWidth 的同一图像资源会被视为缓存中的不同图像。

图片(
  "assets/6392956.jpg",  // 文件路径
  高度: 100,
  宽度: 300,
  缓存高度: (100 * MediaQuery.of(context).devicePixelRatio).toInt(),  // 缓存高度计算
);

为了测试这会对缓存产生什么影响,我们来做一些测量。当前的缓存大小可以通过 PaintingBinding 类访问 PaintingBinding 类。

PaintingBinding 实例的 imageCache 当前占用的字节数

每次测试应用时都进行了彻底重启,以清除缓存里的无关数据

如预期的,较大的图片会增加缓存大小。然而,原始图片非常大,以至于完全未被缓存,这就意味着每次显示时都需要重新解码图片。让我们看看效果。

在这个视频中,每当打开包含图片的屏幕时,超出缓存大小限制的图片都会延迟显示。而对于调整大小后的图像,仅在第一次打开屏幕时显示延迟。

2. 不使用 WebP 图像

另一种优化捆绑资产的方法是使用WebP格式。它可以显著减少图片的文件大小。有很多免费的在线转换工具,并且Flutter本身就支持WebP。

3. 不要在不需要时使用 Opacity 控件

Opacity组件非常有用且方便,但不应在任何情况下随意使用,因为它每次使用时都会新增一层渲染。让我们看看如果这个组件在屏幕上多次出现会发生什么:

        Opacity(  
          opacity: 0.5,  // 尽量不要这样做  
          child: Image.asset(  
            "assets/6392956.jpg", // 图片路径:assets/6392956.jpg  
            height: 100,  // 高度:100  
            width: 300,   // 宽度:300  
          ),  
        );

然后我们打开开发者工具面板,看看渲染图层。

每一层都独立渲染,这导致了大量的多余计算。我们可以将颜色与图像混合来替代使用 Opacity 小部件,正如文档中建议的那样。

    Image.asset(  
      "assets/6392956.jpg",  
      height: 100,  
      width: 300,  
      color: Colors.white.withOpacity(0.5), // <- 就是这里  
      colorBlendMode: BlendMode.modulate, // <- 这里也是  
      cacheHeight: (100 * MediaQuery.of(context).devicePixelRatio).toInt(),  
    ),

这样我们就把所有的图片放在同一层,进行统一渲染:

在使用ColorFiltered小部件时,最好使用colorcolorBlendMode的组合,因为这样可以避免创建新的合成图层。

4. 不预先缓存图片

Flutter 允许我们手动将图片推送到 ImageCache。我们用之前的一张图片试试看。

    TextButton(  
      child: const Text("预缓存图片"),  
      onPressed: () async {  
        var cacheSize = PaintingBinding.instance.imageCache.currentSizeBytes.toString();  
        print('缓存大小: ' + cacheSize);  
        final asset = Image.asset(  
          "assets/6392956.jpg",  
          height: 100,  
          width: 300,  
          cacheHeight: (100 * MediaQuery.of(context).devicePixelRatio).toInt(),  
        );  
        await precacheImage(asset.image, context); // <- 预加载图片  
        print('缓存大小: ' + cacheSize);  
      },  
    )

结果是:

因此,当我们使用相同的解码尺寸打开该界面时,它将立即加载,除非被移出缓存。使用这项技术时要小心,因为缓存空间有限。

5. 不缓存网络图片资源。

如果你的应用从网络获取图片,每次加载这些图片是没有意义的。相反,我们可以使用cached_network_image库或其他任何替代库。该库的文档非常易于理解。

希望这篇文章对你有帮助。我将在找到有用的技巧时不断更新本文。你可以通过关注我的Twitter来了解最新动态。如果你想查看完整的代码,可以查看仓库

0人推荐
随时随地看视频
慕课网APP