flutter-使用extended_image操作图片的加载和状态处理以及缓存和下载

1. 介绍

在 Flutter 的开发过程中,经常会遇到图片的显示和加载处理,通常显示一个图片,都有很多细节需要处理,比如图片的加载、缓存、错误处理、图片的压缩、图片的格式转换等,如果每个地方都手动处理,就太麻烦了,这时候就可以使用糖果大佬的插件 extended_image,它是官方 Image 的扩展三方库,不但支持图片加载以及失败显示,缓存网络图片,缩放拖拽图片,图片浏览等,还支持滑动退出页面,编辑图片(裁剪旋转翻转),保存,绘制自定义效果等功能。

2. 属性介绍

属性描述
url网络请求地址required
key唯一标识符-
semanticLabel语义标签-
excludeFromSemantics是否排除语义false
width宽度-
height高度-
color颜色-
opacity透明度动画-
colorBlendMode颜色混合模式-
fit图片适应方式-
alignment对齐方式Alignment.center
repeat图片重复方式ImageRepeat.noRepeat
centerSlice九宫格切片区域-
matchTextDirection是否匹配文本方向false
gaplessPlayback是否无缝播放false
filterQuality滤镜质量FilterQuality.low
loadStateChanged图片加载状态回调Function
shape盒子形状-
border盒子边框-
borderRadius圆角半径-
clipBehavior裁剪行为Clip.antiAlias
enableLoadState是否启用加载状态true
beforePaintImage图片绘制前回调-
afterPaintImage图片绘制后回调-
mode扩展图片模式(默认/手势/编辑)ExtendedImageMode.none
enableMemoryCache是否启用内存缓存true
clearMemoryCacheIfFailed加载失败时是否清除内存缓存true
onDoubleTap双击事件回调-
initGestureConfigHandler初始化手势配置回调-
enableSlideOutPage是否启用滑动退出页面false
constraints约束条件-
cancelToken用于取消请求的 TokenCancellationToken()
retries请求尝试次数3
timeLimit请求超时时间-
headers请求头-
cache是否缓存true
scale图片缩放比例1.0
timeRetry请求重试间隔milliseconds: 100
extendedImageEditorKey扩展图片编辑器键-
initEditorConfigHandler初始化编辑器配置回调-
heroBuilderForSlidingPage滑动退出页面时的英雄构建器-
clearMemoryCacheWhenDispose销毁时是否清除内存缓存false
handleLoadingProgress是否处理加载进度false
extendedImageGestureKey扩展图片手势键-
cacheWidth缓存宽度-
cacheHeight缓存高度-
isAntiAlias是否开启抗锯齿false
cacheKey缓存键-
printError是否打印错误信息true
compressionRatio压缩比例-
maxBytes最大字节数-
cacheRawData是否缓存原始数据false
imageCacheName图片缓存名称-
cacheMaxAge缓存最大寿命-
layoutInsets布局插入区域EdgeInsets.zero

3. 使用例子

本例子包含如下:

  • 网络图片加载
  • 图片加载中、加载成功、加载失败的处理
  • 放大查看

更详细的使用方式,请参考extended_image 文档

ExtendedImage.network(
  imagePath,
  width: double.infinity,
  fit: BoxFit.fitHeight,
  cache: true,
  mode: ExtendedImageMode.gesture,
  initGestureConfigHandler: (state) {
    return GestureConfig(
      // 缩放最小值
      minScale: 1.0,
      // 缩放动画最小值 当缩放结束时回到 minScale 值
      animationMinScale: 0.8,
      // 缩放最大值
      maxScale: 2.0,
      // 缩放动画最大值 当缩放结束时回到 maxScale 值
      animationMaxScale: 2.5,
      // 缩放拖拽速度
      speed: 1.0,
      // 拖拽惯性速度
      inertialSpeed: 100.0,
      initialScale: 1.0,
      // 是否使用 ExtendedImageGesturePageView 展示图片
      inPageView: false,
      // 当图片的初始化缩放大于 1.0 的时候,根据相对位置初始化图片
      initialAlignment: InitialAlignment.center
    );
  }/// 加载状态回调
  loadStateChanged: (ExtendedImageState state) {
    switch (state.extendedImageLoadState) {
      /// 加载中
      case LoadState.loading:
        // 自己写的加载中的Loading组件
        return LoadWait();

      /// 加载成功
      case LoadState.completed:
        return state.completedWidget;

      /// 加载失败
      case LoadState.failed:
        // 自己写的加载失败的组件 并且把重试的回调传递过去
        return LoadFailed(callback: state.reLoadImage);
    }
  }
)

4. 获取缓存数据并下载

这里是读取缓存数据然后下载,下载使用了(image_gallery_saver)[https://pub.dev/packages/image_gallery_saver]这个库。如果缓存里没有,则换成Dio下载。

import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:extended_image/extended_image.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';

/// 下载网络图片(先读缓存资源,缓存没有再重新获取资源)
Future<bool> downloadNetworkImage(String imagePath) async {
  const String prefix = 'App-Save';
  // 获取缓存图片
  var cacheData = await getNetworkImageData(imagePath, useCache: true);
  // 获取当前时间戳
  int timestamp = DateTime.now().millisecondsSinceEpoch;
  // 将时间戳转换为可读的日期格式
  String dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp).toString();
  // 拼接名字
  String saveName = '$prefix-$dateTime';
  // 下载逻辑
  late dynamic result;
  // 如果缓存图片不为空
  if (cacheData != null) {
    result = await ImageGallerySaver.saveImage(cacheData,
        quality: 100, name: saveName);
  } else {
    var response = await Dio()
        .get(imagePath, options: Options(responseType: ResponseType.bytes));
    result = await ImageGallerySaver.saveImage(
        Uint8List.fromList(response.data),
        quality: 100,
        name: saveName);
  }
  return result['isSuccess'] ? true:false;
}

5. 完整封装

封装思路:关键参数必传,次要参数有默认值,统一加载中、加载失败、加载成功的状态,统一处理图片加载失败的情况,统一处理图片加载成功的情况

封装代码:

注意,这里使用了屏幕适配插件 flutter_screenutil,全局使用.w 做尺寸单位。

import 'package:flutter/material.dart';
import 'package:extended_image/extended_image.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

/// 加载网络图片
class MyLoadNetworkImage extends StatelessWidget {
  /// 图片地址
  final String imageUrl;

  /// 背景色
  final Color bgColor;

  /// 失败回调
  final Function()? onFailed;

  /// 成功回调
  final Function()? onSucceed;

  /// 宽度
  final double? width;

  /// 高度
  final double? height;

  /// 是否缓存
  final bool cache;

  /// 图片适应方式
  final BoxFit fit;

  /// 图片模式
  final ExtendedImageMode mode;

  /// 图片手势设置 需配合 mode = ExtendedImageMode.gesture
  final GestureConfig Function(ExtendedImageState)? initGestureConfigHandler;

  /// 图片Load动画
  final bool loadAnime;

  /// 压缩率
  final double compressionRatio;

  /// 缓存宽度
  final int? cacheWidth;

  /// 缓存高度
  final int? cacheHeight;

  /// Load图宽高
  final double loadBoxWAndH;

  const MyLoadNetworkImage(
      {Key? key,
      required this.imageUrl,
      this.onFailed,
      this.onSucceed,
      this.bgColor = Colors.black,
      this.width,
      this.height,
      this.cache = true,
      this.fit = BoxFit.fitHeight,
      this.mode = ExtendedImageMode.none,
      this.initGestureConfigHandler,
      this.loadAnime = false,
      this.compressionRatio = 1.0,
      this.cacheWidth,
      this.cacheHeight,
      this.loadBoxWAndH = 64})
      : super(key: key);

  
  Widget build(BuildContext context) {
    return ExtendedImage.network(
      imageUrl,
      width: width,
      height: height,
      fit: fit,
      cache: cache,
      initGestureConfigHandler: initGestureConfigHandler,
      mode: mode,
      compressionRatio: compressionRatio,
      cacheMaxAge: Duration(hours: 24),
      cacheWidth: cacheWidth,
      cacheHeight: cacheHeight,

      /// 加载状态回调
      loadStateChanged: (ExtendedImageState state) {
        switch (state.extendedImageLoadState) {
          /// 加载中
          case LoadState.loading:
            return loadAnime
                ? Container(
                    constraints: BoxConstraints.expand(),
                    alignment: Alignment.center,
                    child: SvgPicture.asset(xxxxx.loadingImage),
                  )
                : WaitOrLoadWidget(radius: loadBoxWAndH);

          /// 加载成功
          case LoadState.completed:
            if (onSucceed != null) {
              onSucceed!();
            }
            return state.completedWidget;

          /// 加载失败
          case LoadState.failed:
            if (onFailed != null) {
              onFailed!();
            }
            return LoadFailedWidget(callback: state.reLoadImage);
        }
      },
    );
  }
}

/// 等待中或加载中的组件
class WaitOrLoadWidget extends StatelessWidget {
  final double radius;
  const WaitOrLoadWidget({Key? key, this.radius = 64}) : super(key: key);

  
  Widget build(BuildContext context) {
    double myRadius = radius == 64 ? 64.w : radius;

    return Container(
      color: Colors.black,
      alignment: Alignment.center,
      constraints: BoxConstraints.expand(),
      child: SizedBox(
        width: myRadius,
        height: myRadius,
        child: SvgPicture.asset(xxxxx.loadImage),
      ),
    );
  }
}

/// 加载失败的组件
class LoadFailedWidget extends StatelessWidget {
  final double radius;

  /// 回调函数
  final Function() callback;

  const LoadFailedWidget({Key? key, required this.callback, this.radius = 64})
      : super(key: key);

  
  Widget build(BuildContext context) {
    double myRadius = radius == 64 ? 64.w : radius;

    return Container(
      color: Colors.black,
      alignment: Alignment.center,
      constraints: BoxConstraints.expand(),
      child: GestureDetector(
        onTap: callback,
        child: SizedBox(
          width: myRadius,
          height: myRadius,
          child: SvgPicture.asset(xxxxx.failedImage),
        ),
      ),
    );
  }
}

本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~

往期文章

个人主页

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Flutter Zoomable Image 是一个用于 Flutter 应用程序的库,它提供了一个可缩放和拖动的图像小部件。使用 Flutter Zoomable Image,您可以轻松地实现图像的缩放、拖动和捏放手势操作。这对于创建具有可交互性的图像查看器和画廊等应用程序非常有用。 要使用 Flutter Zoomable Image,您需要在项目的 `pubspec.yaml` 文件中添加依赖项,并运行 `flutter packages get` 命令来获取库。 以下是一个简单的示例代码,演示了如何在 Flutter使用 Zoomable Image: ```dart import 'package:flutter/material.dart'; import 'package:flutter_zoomable_image/flutter_zoomable_image.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Zoomable Image Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Zoomable Image Demo'), ), body: Center( child: ZoomableImage( AssetImage('path/to/your/image.jpg'), placeholder: Center(child: CircularProgressIndicator()), backgroundColor: Colors.black, ), ), ); } } ``` 在上面的示例中,我们创建了一个简单的 Flutter 应用程序,其中包含一个使用 ZoomableImage 小部件的页面。ZoomableImage 接受一个 AssetImage 对象作为图像源,并提供了一些可选参数,例如 placeholder(用于在图像加载期间显示的小部件)和 backgroundColor(用于设置图像背景色)。 您可以根据自己的需求定制 Zoomable Image 的样式和行为。要了解更多关于 Flutter Zoomable Image 的信息和用法,请参考官方文档或库的 GitHub 页面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鹏多多.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值