当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2023-11-12 16:31:03 221观看
导读Image widget 是 Flutter 中最常用的 widget 之一,但我相信我们没有充分利用它的功能,仅仅显示一个图片是不够的,你还应该给用户他们需要的最佳体验!在这篇文章中,我将谈论一些图像技巧和最佳实践,以获得更好的性能和用户体

Image widget 是 Flutter 中最常用的 widget 之一,但我相信我们没有充分利用它的功能,仅仅显示一个图片是不够的,你还应该给用户他们需要的最佳体验!lce28资讯网——每日最新资讯28at.com

在这篇文章中,我将谈论一些图像技巧和最佳实践,以获得更好的性能和用户体验。lce28资讯网——每日最新资讯28at.com

这些技巧是:lce28资讯网——每日最新资讯28at.com

  • 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%,并且比其他格式快。lce28资讯网——每日最新资讯28at.com

这意味着,你的应用程序将使用更少的内存,构建速度更快。lce28资讯网——每日最新资讯28at.com

这里有一些基准:lce28资讯网——每日最新资讯28at.com

图片图片lce28资讯网——每日最新资讯28at.com

图片图片lce28资讯网——每日最新资讯28at.com

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

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

它可以防止应用程序出现布局偏移lce28资讯网——每日最新资讯28at.com

图片图片lce28资讯网——每日最新资讯28at.com

之前——之后lce28资讯网——每日最新资讯28at.com

Imagework(  imageUrl,  width: 200,  height: 150,);

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

图片图片lce28资讯网——每日最新资讯28at.com

图片图片lce28资讯网——每日最新资讯28at.com

你的图片可能会导致设备内存膨胀,这是因为,尽管它们在 UI 中占据相对较小的一部分,Flutter 还是会以全分辨率渲染它们,从而消耗大量内存。lce28资讯网——每日最新资讯28at.com

为了避免这种问题,可以使用 cacheWidth 或 cacheHeight 参数对指定大小的图像进行解码。lce28资讯网——每日最新资讯28at.com

此外,我们可以使用 Flutter 开发者工具轻松检测超大图像。lce28资讯网——每日最新资讯28at.com

如果图像过大,它会反转图像,使其颠倒。lce28资讯网——每日最新资讯28at.com

注意!缓存大小不应该小于小部件的大小,否则,由于分辨率低,它看起来像素化!lce28资讯网——每日最新资讯28at.com

Imagework(  imageUrl,  cacheWidth: 100,  cacheHeight: 150,);

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

如果你在显示图像之前缓存它们,Flutter 将跳过构建的处理步骤并立即显示它们。lce28资讯网——每日最新资讯28at.com

图片图片lce28资讯网——每日最新资讯28at.com

class MyImage extends StatefulWidget {  const MyImage({super.key});  @override  State createState() => _MyImageState();}class _MyImageState extends State {  late final Image myImage;  @override  void initState() {    super.initState();    myImage = Image.asset('path');  }  @override  void didChangeDependencies() {    super.didChangeDependencies();    precacheImage(myImage.image, context);  }  @override  Widget build(BuildContext context) {    return myImage;  }}

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

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

图片图片lce28资讯网——每日最新资讯28at.com

return Imagework(  imageUrl,  loadingBuilder: (_, child, event) {    if (event == null) return child;    return const Center(child: CircularProgressIndicator());  },);

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

我们也可以显示进度百分比,而不是无限加载,这样对用户来说更有用。lce28资讯网——每日最新资讯28at.com

图片图片lce28资讯网——每日最新资讯28at.com

return Imagework(  imageUrl,  loadingBuilder: (_, child, event) {    if (event == null) return child;    return Center(      child: CircularProgressIndicator(        value: event.cumulativeBytesLoaded / (event.expectedTotalBytes ?? 0),      ),    );  },);

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

显示进度条是好的,但并不是最好的选择,显示闪烁效果(Shimmer)要比显示进度条好得多。lce28资讯网——每日最新资讯28at.com

图片图片lce28资讯网——每日最新资讯28at.com

return Imagework(  imageUrl,  height: 200,  width: 350,  loadingBuilder: (_, child, event) {    if (event == null) return child;    return const 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,  });  final double? width;  final double? height;  final double minOpacity;  final double maxOpacity;  final BorderRadius? borderRadius;  final Widget? child;  @override  State createState() => _ShimmerState();}class _ShimmerState extends State with SingleTickerProviderStateMixin {  late final AnimationController controller;  @override  void initState() {    super.initState();    controller = AnimationController(      vsync: this,      duration: const Duration(seconds: 1),      lowerBound: widget.minOpacity,      upperBound: widget.maxOpacity,    )..repeat(reverse: true);  }  @override  void dispose() {    controller.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return RepaintBoundary(      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 效果。lce28资讯网——每日最新资讯28at.com

lce28资讯网——每日最新资讯28at.com

官方文档:创建 shimmer 加载效果(https://docs.flutter.dev/cookbook/effects/shimmer-loading)lce28资讯网——每日最新资讯28at.com

8.显示 blurhash 作为占位符

为了改善用户体验,可以使用哈希代码显示图像的模糊版本,而不是在图像加载时显示空白的灰色区域。lce28资讯网——每日最新资讯28at.com

图片图片lce28资讯网——每日最新资讯28at.com

https://blurha.sh/lce28资讯网——每日最新资讯28at.com

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

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

默认情况下,图片加载后立即显示,这对我们的视觉体验来说非常糟糕,为了改善这一点,我们可以用一个小的渐入动画来显示它们。lce28资讯网——每日最新资讯28at.com

我们可以使用 FadeInImage 来实现这个功能。lce28资讯网——每日最新资讯28at.com

它需要字节或资源作为占位符,在这个例子中,我将使用 transparent_image 包来获取透明图像字节。lce28资讯网——每日最新资讯28at.com

我们还可以使用 cached_network_image 包来实现这一点,以及更多。lce28资讯网——每日最新资讯28at.com

return FadeInImage.memoryNetwork(  image: imageUrl,  placeholder: kTransparentImage,);// 或者return CachedNetworkImage(  imageUrl: imageUrl,);

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

为了避免每次下载相同的图片,我们可以缓存第一次下载的图片并重复使用,为了实现这一点,我们可以创建自己的缓存机制,或者我们可以直接使用 cached_network_image。lce28资讯网——每日最新资讯28at.com

它缓存网络图像,默认情况下自动显示它们的淡入效果,并提供了更多的图像控制。lce28资讯网——每日最新资讯28at.com

11.注意非经常性成本

Image widget 没有 const 构造函数,虽然这在大多数情况下都不是问题,但我们可以通过将它包装在自定义 widget 中来修复它。lce28资讯网——每日最新资讯28at.com

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

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

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

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

图片图片lce28资讯网——每日最新资讯28at.com

class MyImage extends StatefulWidget {  const MyImage({super.key});  @override  State createState() => _MyImageState();}class _MyImageState extends State {  int attempt = 0;  @override  Widget build(BuildContext context) {    return CachedNetworkImage(      imageUrl: imageUrl,      cacheKey: '$attempt',      height: 200,      width: 250,      fit: BoxFit.cover,      errorWidget: (_, __, ___) {        return RetryWidget(          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,  });  final double? height;  final double? width;  final void Function() onTap;  @override  Widget build(BuildContext context) {    return GestureDetector(      onTap: onTap,      child: Container(        height: height,        width: width,        alignment: Alignment.center,        decoration: const BoxDecoration(color: Colors.black12),        child: const Column(          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,将它们随意组合并自由使用!lce28资讯网——每日最新资讯28at.com

原文:https://medium.com/itnext/12-image-tips-and-best-practices-for-the-best-ux-performance-in-flutter-e7a1b2b1da2a&strip=0&vwsrc=1&referer=medium-parserlce28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-22455-0.html在 Flutter 中实现最佳 UX 性能的 12 个图像技巧和最佳实践

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Json格式弊端及优化方案

下一篇: 五个提高Java代码安全性的VS Code插件

标签:
  • 热门焦点
Top