在 Flutter 中实现最佳 UX 性能的 12 个图像技巧和最佳实践

仅仅展示图片是不够的!您还应该为用户提供他们需要的最佳体验!因此,我强烈建议您创建自己的自定义图片
首页 新闻资讯 行业资讯 在 Flutter 中实现最佳 UX 性能的 12 个图像技巧和最佳实践

Image widget 是 Flutter 中最常用的 widget 之一,但我相信我们没有充分利用它的功能,仅仅显示一个图片是不够的,你还应该给用户他们需要的最佳体验!

在这篇文章中,我将谈论一些图像技巧和最佳实践,以获得更好的性能和用户体验。

这些技巧是:

  • 1.使用 WebP 而不是 JPG/PNG

  • 2.设置宽度和高度以保留 UI 空间

  • 3.降低图片的显示分辨率以减少内存使用

  • 4.预加载/预缓存您的图像,以便即时加载图像

  • 5.加载时显示进度指示器

  • 6.加载时显示进度百分比指示器

  • 7.加载时显示闪烁效果,以提高用户体验

  • 8.显示 blurhash 作为占位符

  • 9.使用渐变效果来提高用户体验

  • 10.缓存图像以减少网络使用并提高性能

  • 11.注意非经常性成本

  • 12.在失败时显示重试按钮

  • 结语

1.使用 WebP 而不是 JPG/PNG

WebP 是下一代图像格式,它比 PNG 和 JPEG 小约 25%,并且比其他格式快。

这意味着,你的应用程序将使用更少的内存,构建速度更快。

这里有一些基准:

图片图片

图片图片

Image.asset(// 'image.jpg','image.webp',// PREFER);

2.设置宽度和高度以保留 UI 空间

它可以防止应用程序出现布局偏移

图片图片

之前——之后

Image.network(imageUrl,width:200,height:150,);

3.降低图片的显示分辨率以减少内存使用

图片图片

图片图片

你的图片可能会导致设备内存膨胀,这是因为,尽管它们在 UI 中占据相对较小的一部分,Flutter 还是会以全分辨率渲染它们,从而消耗大量内存。

为了避免这种问题,可以使用 cacheWidth 或 cacheHeight 参数对指定大小的图像进行解码。

此外,我们可以使用 Flutter 开发者工具轻松检测超大图像。

如果图像过大,它会反转图像,使其颠倒。

注意!缓存大小不应该小于小部件的大小,否则,由于分辨率低,它看起来像素化!

Image.network(
  imageUrl,
  cacheWidth: 100,
  cacheHeight: 150,
);

4.预加载/预缓存您的图像,以便即时加载图像

如果你在显示图像之前缓存它们,Flutter 将跳过构建的处理步骤并立即显示它们。

图片图片

class MyImage extends StatefulWidget {
  const MyImage({super.key});@overrideState createState()=>_MyImageState();}

class _MyImageState extends State {
  late final Image myImage;@overridevoid initState(){
    super.initState();myImage=Image.asset('path');}@overridevoid didChangeDependencies(){
    super.didChangeDependencies();precacheImage(myImage.image,context);}@overrideWidget build(BuildContext context){returnmyImage;}
}

5.加载时显示进度指示器

突然弹出图像不是预期的行为,用户可能会因为网络连接不足而错过图像并向下滚动,或者可能在屏幕上看到一些空白,等等。我们应该始终通知用户图像正在加载。

图片图片

returnImage.network(imageUrl,loadingBuilder:(_,child,event){if(event==null)returnchild;returnconst Center(child: CircularProgressIndicator());},);

6.加载时显示进度百分比指示器

我们也可以显示进度百分比,而不是无限加载,这样对用户来说更有用。

图片图片

returnImage.network(imageUrl,loadingBuilder:(_,child,event){if(event==null)returnchild;returnCenter(child: CircularProgressIndicator(value: event.cumulativeBytesLoaded/(event.expectedTotalBytes ??0),),);},);

7.加载时显示闪烁效果,以提高用户体验

显示进度条是好的,但并不是最好的选择,显示闪烁效果(Shimmer)要比显示进度条好得多。

图片图片

returnImage.network(imageUrl,height:200,width:350,loadingBuilder:(_,child,event){if(event==null)returnchild;returnconst Shimmer(height:200,width:350,);});// Most Basic Shimmerclass Shimmer extends StatefulWidget {
  const Shimmer({
    super.key,this.width,this.height,this.minOpacity=0.015,this.maxOpacity=0.15,this.borderRadius=const BorderRadius.all(Radius.circular(4)),this.child,});finaldouble? width;finaldouble? height;finaldoubleminOpacity;finaldoublemaxOpacity;final BorderRadius? borderRadius;final Widget? child;@overrideState createState()=>_ShimmerState();}

class _ShimmerState extends StatewithSingleTickerProviderStateMixin {
  late final AnimationController controller;@overridevoid initState(){
    super.initState();controller=AnimationController(vsync: this,duration: const Duration(seconds:1),lowerBound: widget.minOpacity,upperBound: widget.maxOpacity,)..repeat(reverse:true);}@overridevoid dispose(){
    controller.dispose();super.dispose();}@overrideWidget build(BuildContext context){returnRepaintBoundary(child: FadeTransition(opacity: controller,child: Container(width: widget.width,height: widget.height,decoration: BoxDecoration(color: Colors.black,borderRadius: widget.borderRadius,),child: widget.child,),),);}
}

我创建了一个简单的 shimmer 小部件,但你可以从官方文档中学习如何创建高级版本的 shimmer 效果。

官方文档:创建 shimmer 加载效果(https://docs.flutter.dev/cookbook/effects/shimmer-loading)

8.显示 blurhash 作为占位符

为了改善用户体验,可以使用哈希代码显示图像的模糊版本,而不是在图像加载时显示空白的灰色区域。

图片图片

https://blurha.sh/

returnconst SizedBox(width:350,height:200,child: BlurHash(hash: hashCode,imageFit: BoxFit.cover,image: imageUrl,),);

9.使用渐变效果来提高用户体验

默认情况下,图片加载后立即显示,这对我们的视觉体验来说非常糟糕,为了改善这一点,我们可以用一个小的渐入动画来显示它们。

我们可以使用 FadeInImage 来实现这个功能。

它需要字节或资源作为占位符,在这个例子中,我将使用 transparent_image 包来获取透明图像字节。

我们还可以使用 cached_network_image 包来实现这一点,以及更多。

returnFadeInImage.memoryNetwork(image: imageUrl,placeholder: kTransparentImage,);// 或者returnCachedNetworkImage(imageUrl: imageUrl,);

10.缓存图像以减少网络使用并提高性能

为了避免每次下载相同的图片,我们可以缓存第一次下载的图片并重复使用,为了实现这一点,我们可以创建自己的缓存机制,或者我们可以直接使用 cached_network_image。

它缓存网络图像,默认情况下自动显示它们的淡入效果,并提供了更多的图像控制。

11.注意非经常性成本

Image widget 没有 const 构造函数,虽然这在大多数情况下都不是问题,但我们可以通过将它包装在自定义 widget 中来修复它。

它不仅可以让我们的应用程序更具性能,而且我们还可以根据我们的意愿定制小部件,例如,我们可以为每个图像小部件创建一个全局解决方案,而不是每次都处理 error/loading 情况。

enum_ImageType { asset,network }

class AppImage extends StatelessWidget {
  const AppImage.asset(this.image,{
    super.key,}):type=_ImageType.asset;const AppImage.network(this.image,{
    super.key,}):type=_ImageType.network;final String image;final _ImageTypetype;@overrideWidget build(BuildContext context){
    const errorWidget=Icon(Icons.error);returnswitch(type){
      _ImageType.asset=>Image.asset(image,errorBuilder:(_,__,___)=>errorWidget,),_ImageType.network=>Image.network(image,errorBuilder:(_,__,___)=>errorWidget,),};}
}/// 使用const AppImage.asset(''),// OKconst Image.asset(''),// 错误!!Image 没有const构造函数// 正如您所知,拥有 const 的小部件非常重要以获得更好的性能。

12.在失败时显示重试按钮

有时候由于网络连接不好或其他原因,图片无法第一次加载,显示错误消息是好的,但这还不够,如果我们想把应用的 UX 提升到另一个层次,我们应该让用户重新加载图片,并继续使用应用,而不会遇到任何麻烦。

图片图片

class MyImage extends StatefulWidget {
  const MyImage({super.key});@overrideState createState()=>_MyImageState();}

class _MyImageState extends State {intattempt=0;@overrideWidget build(BuildContext context){returnCachedNetworkImage(imageUrl: imageUrl,cacheKey:'$attempt',height:200,width:250,fit: BoxFit.cover,errorWidget:(_,__,___){returnRetryWidget(height:200,width:250,onTap:()=>setState(()=>attempt++),);},);}
}// Just a basic retry buttonclass RetryWidget extends StatelessWidget {
  const RetryWidget({
    super.key,required this.height,required this.width,required this.onTap,});finaldouble? height;finaldouble? width;final voidFunction()onTap;@overrideWidget build(BuildContext context){returnGestureDetector(onTap: onTap,child: Container(height: height,width: width,alignment: Alignment.center,decoration: const BoxDecoration(color: Colors.black12),child: constColumn(mainAxisSize: MainAxisSize.min,children:[Icon(Icons.image_not_supported,size:20),SizedBox(height:12),Padding(padding: EdgeInsets.symmetric(horizontal:12),child:Text("Image couldn't load, tap here to retry",textAlign: TextAlign.center,style: TextStyle(fontSize:14,color: Colors.black),),),],),),);}
}

结语

仅仅展示图片是不够的!您还应该为用户提供他们需要的最佳体验!因此,我强烈建议您创建自己的自定义图片 widget,将它们随意组合并自由使用!

原文:https://medium.com/itnext/12-image-tips-and-best-practices-for-the-best-ux-performance-in-flutter-e7a1b2b1da2a&strip=0&vwsrc=1&referer=medium-parser

11    2023-11-12 11:54:55    UX 性能 widget