Image widget 是 Flutter 中最常用的 widget 之一,但我相信我们没有充分利用它的功能,仅仅显示一个图片是不够的,你还应该给用户他们需要的最佳体验!
在这篇文章中,我将谈论一些图像技巧和最佳实践,以获得更好的性能和用户体验。
这些技巧是:
WebP 是下一代图像格式,它比 PNG 和 JPEG 小约 25%,并且比其他格式快。
这意味着,你的应用程序将使用更少的内存,构建速度更快。
这里有一些基准:
图片
图片
Image.asset( // 'image.jpg', 'image.webp', // PREFER);
它可以防止应用程序出现布局偏移
图片
之前——之后
Imagework( imageUrl, width: 200, height: 150,);
图片
图片
你的图片可能会导致设备内存膨胀,这是因为,尽管它们在 UI 中占据相对较小的一部分,Flutter 还是会以全分辨率渲染它们,从而消耗大量内存。
为了避免这种问题,可以使用 cacheWidth 或 cacheHeight 参数对指定大小的图像进行解码。
此外,我们可以使用 Flutter 开发者工具轻松检测超大图像。
如果图像过大,它会反转图像,使其颠倒。
注意!缓存大小不应该小于小部件的大小,否则,由于分辨率低,它看起来像素化!
Imagework( imageUrl, cacheWidth: 100, cacheHeight: 150,);
如果你在显示图像之前缓存它们,Flutter 将跳过构建的处理步骤并立即显示它们。
图片
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; }}
突然弹出图像不是预期的行为,用户可能会因为网络连接不足而错过图像并向下滚动,或者可能在屏幕上看到一些空白,等等。我们应该始终通知用户图像正在加载。
图片
return Imagework( imageUrl, loadingBuilder: (_, child, event) { if (event == null) return child; return const Center(child: CircularProgressIndicator()); },);
我们也可以显示进度百分比,而不是无限加载,这样对用户来说更有用。
图片
return Imagework( imageUrl, loadingBuilder: (_, child, event) { if (event == null) return child; return Center( child: CircularProgressIndicator( value: event.cumulativeBytesLoaded / (event.expectedTotalBytes ?? 0), ), ); },);
显示进度条是好的,但并不是最好的选择,显示闪烁效果(Shimmer)要比显示进度条好得多。
图片
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 效果。
❝
官方文档:创建 shimmer 加载效果(https://docs.flutter.dev/cookbook/effects/shimmer-loading)
为了改善用户体验,可以使用哈希代码显示图像的模糊版本,而不是在图像加载时显示空白的灰色区域。
图片
https://blurha.sh/
return const SizedBox( width: 350, height: 200, child: BlurHash( hash: hashCode, imageFit: BoxFit.cover, image: imageUrl, ),);
默认情况下,图片加载后立即显示,这对我们的视觉体验来说非常糟糕,为了改善这一点,我们可以用一个小的渐入动画来显示它们。
我们可以使用 FadeInImage 来实现这个功能。
它需要字节或资源作为占位符,在这个例子中,我将使用 transparent_image 包来获取透明图像字节。
我们还可以使用 cached_network_image 包来实现这一点,以及更多。
return FadeInImage.memoryNetwork( image: imageUrl, placeholder: kTransparentImage,);// 或者return CachedNetworkImage( imageUrl: imageUrl,);
为了避免每次下载相同的图片,我们可以缓存第一次下载的图片并重复使用,为了实现这一点,我们可以创建自己的缓存机制,或者我们可以直接使用 cached_network_image。
它缓存网络图像,默认情况下自动显示它们的淡入效果,并提供了更多的图像控制。
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 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 的小部件非常重要以获得更好的性能。
有时候由于网络连接不好或其他原因,图片无法第一次加载,显示错误消息是好的,但这还不够,如果我们想把应用的 UX 提升到另一个层次,我们应该让用户重新加载图片,并继续使用应用,而不会遇到任何麻烦。
图片
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,将它们随意组合并自由使用!
原文: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
本文链接:http://www.28at.com/showinfo-26-22455-0.html在 Flutter 中实现最佳 UX 性能的 12 个图像技巧和最佳实践
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: Json格式弊端及优化方案