Flutter图片增强功能插件extended_image的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter图片增强功能插件extended_image的使用

extended_image

pub package GitHub stars GitHub forks GitHub license GitHub issues

ExtendedImage 是一个强大的Flutter图像扩展库,支持加载占位图(loading)/失败状态、缓存网络图片、缩放平移图片、照片查看器、滑出页面、编辑器(裁剪、旋转、翻转)、自定义绘制等功能。

Web演示

ExtendedImage Web Demo

目录

导入

Null Safety版本

environment:
  sdk: '>=2.12.0 <3.0.0'
  flutter: '>=2.0'
dependencies:
  extended_image: ^4.0.0

非Null Safety版本

environment:
  sdk: '>=2.6.0 <2.12.0'
  flutter: '>1.17.0 <=1.22.6'
dependencies:
  extended_image: ^3.0.0-non-null-safety

缓存网络图片

简单使用

你可以像使用Image Widget一样使用ExtendedImage.network

ExtendedImage.network(
  url,
  width: ScreenUtil.instance.setWidth(400),
  height: ScreenUtil.instance.setHeight(400),
  fit: BoxFit.fill,
  cache: true,
  border: Border.all(color: Colors.red, width: 1.0),
  shape: BoxShape.rectangle,
  borderRadius: BorderRadius.all(Radius.circular(30.0)),
)

使用ExtendedNetworkImageProvider

ExtendedNetworkImageProvider(
  this.url, {
  this.scale = 1.0,
  this.headers,
  this.cache: false,
  this.retries = 3,
  this.timeLimit,
  this.timeRetry = const Duration(milliseconds: 100),
  CancellationToken cancelToken,
}) : assert(url != null),
     assert(scale != null),
     cancelToken = cancelToken ?? CancellationToken();
参数 描述 默认值
url 图片的URL required
scale 放置在ImageInfo对象中的比例 1.0
headers 用于通过HttpClient.get从网络获取图片时使用的HTTP头部信息 -
cache 是否将图片缓存到本地 false
retries 请求重试次数 3
timeLimit 请求图片的时间限制 -
timeRetry 请求重试的时间间隔 milliseconds: 100
cancelToken 取消网络请求的令牌 CancellationToken()

你可以创建新的提供者并继承ExtendedProvider,并覆盖instantiateImageCodec方法。这样你就可以在这里处理原始图片数据(压缩图片)。

加载状态

Extended Image 提供了三种状态(loading, completed, failed),你可以通过loadStateChanged回调定义自己的状态Widget。

ExtendedImage.network(
  url,
  loadStateChanged: (ExtendedImageState state) {
    switch (state.extendedImageLoadState) {
      case LoadState.loading:
        return Image.asset("assets/loading.gif", fit: BoxFit.fill);
      case LoadState.completed:
        return FadeTransition(
          opacity: _controller,
          child: ExtendedRawImage(
            image: state.extendedImageInfo?.image,
            width: ScreenUtil.instance.setWidth(600),
            height: ScreenUtil.instance.setHeight(400),
          ),
        );
      case LoadState.failed:
        return GestureDetector(
          child: Stack(
            fit: StackFit.expand,
            children: [
              Image.asset("assets/failed.jpg", fit: BoxFit.fill),
              Positioned(
                bottom: 0.0,
                left: 0.0,
                right: 0.0,
                child: Text(
                  "load image failed, click to reload",
                  textAlign: TextAlign.center,
                ),
              )
            ],
          ),
          onTap: () {
            state.reLoadImage();
          },
        );
    }
  },
)

缩放平移

双击动画

onDoubleTap: (ExtendedImageGestureState state) {
  var pointerDownPosition = state.pointerDownPosition;
  double begin = state.gestureDetails.totalScale;
  double end;

  _animation?.removeListener(animationListener);
  _animationController.stop();
  _animationController.reset();

  if (begin == doubleTapScales[0]) {
    end = doubleTapScales[1];
  } else {
    end = doubleTapScales[0];
  }

  animationListener = () {
    state.handleDoubleTap(
        scale: _animation.value,
        doubleTapPosition: pointerDownPosition);
  };
  _animation = _animationController
      .drive(Tween<double>(begin: begin, end: end));

  _animation.addListener(animationListener);

  _animationController.forward();
},

编辑器

ExtendedImage.network(
  imageTestUrl,
  fit: BoxFit.contain,
  mode: ExtendedImageMode.editor,
  initEditorConfigHandler: (state) {
    return EditorConfig(
        maxScale: 8.0,
        cropRectPadding: EdgeInsets.all(20.0),
        hitTestSize: 20.0,
        cropAspectRatio: _aspectRatio.aspectRatio,
    );
  },
);

裁剪数据

Dart库(stable)

  1. pubspec.yaml中添加依赖:

    dependencies:
      image: any
    
  2. 获取裁剪矩形和原始图片数据:

    final Rect cropRect = state.getCropRect();
    var data = state.rawImageData;
    
  3. 将原始图片数据转换为image库的数据:

    final lb = await loadBalancer;
    Image src = await lb.run<Image, List<int>>(decodeImage, data);
    
  4. 对数据进行裁剪、翻转、旋转:

    image = bakeOrientation(image);
    if (editAction.hasRotateDegrees) {
      image = copyRotate(image, angle: editAction.rotateDegrees);
    }
    
    if (editAction.flipY) {
      image = flip(image, direction: FlipDirection.horizontal);
    }
    
    if (editAction.needCrop) {
      image = copyCrop(
        image,
        x: cropRect.left.toInt(),
        y: cropRect.top.toInt(),
        width: cropRect.width.toInt(),
        height: cropRect.height.toInt(),
      );
    }
    
  5. 转换回原始图片数据:

    var fileData = await lb.run<List<int>, Image>(encodeJpg, src);
    

原生库(faster)

  1. pubspec.yaml中添加依赖:

    dependencies:
      image_editor: any
    
  2. 获取裁剪矩形和原始图片数据:

    final Rect cropRect = state.getCropRect();
    var data = state.rawImageData;
    
  3. 准备裁剪选项:

    if (action.hasRotateDegrees) {
      final int rotateDegrees = action.rotateDegrees.toInt();
      option.addOption(RotateOption(rotateDegrees));
    }
    if (action.flipY) {
      option.addOption(const FlipOption(horizontal: true, vertical: false));
    }
    
    if (action.needCrop) {
      Rect cropRect = imageEditorController.getCropRect()!;
      option.addOption(ClipOption.fromRect(cropRect));
    }
    
  4. 使用editImage进行裁剪:

    final result = await ImageEditor.editImage(
      image: img,
      imageEditorOption: option,
    );
    

照片查看器

ExtendedImageGesturePageView.builder(
  itemBuilder: (BuildContext context, int index) {
    var item = widget.pics[index].picUrl;
    Widget image = ExtendedImage.network(
      item,
      fit: BoxFit.contain,
      mode: ExtendedImageMode.gesture,
      gestureConfig: GestureConfig(
        inPageView: true, initialScale: 1.0,
        cacheGesture: false
      ),
    );
    image = Container(
      child: image,
      padding: EdgeInsets.all(5.0),
    );
    if (index == currentIndex) {
      return Hero(
        tag: item + index.toString(),
        child: image,
      );
    } else {
      return image;
    }
  },
  itemCount: widget.pics.length,
  onPageChanged: (int index) {
    currentIndex = index;
    rebuild.add(index);
  },
  controller: PageController(
    initialPage: currentIndex,
  ),
  scrollDirection: Axis.horizontal,
),

滑出页面

启用滑出页面

return ExtendedImageSlidePage(
  child: result,
  slideAxis: SlideAxis.both,
  slideType: SlideType.onlyImage,
  onSlidingPage: (state) {
    var showSwiper = !state.isSliding;
    if (showSwiper != _showSwiper) {
      _showSwiper = showSwiper;
      rebuildSwiper.add(_showSwiper);
    }
  },
);

使用透明页面路由推送

Navigator.push(
  context,
  Platform.isAndroid
      ? TransparentMaterialPageRoute(builder: (_) => page)
      : TransparentCupertinoPageRoute(builder: (_) => page),
);

边框、圆角、形状

ExtendedImage.network(
  url,
  width: ScreenUtil.instance.setWidth(400),
  height: ScreenUtil.instance.setHeight(400),
  fit: BoxFit.fill,
  cache: true,
  border: Border.all(color: Colors.red, width: 1.0),
  shape: BoxShape.rectangle,
  borderRadius: BorderRadius.all(Radius.circular(30.0)),
),

清除和保存

清除

// 清除磁盘缓存
clearDiskCachedImages(duration: const Duration(days: 7));

// 清除特定URL的磁盘缓存
clearDiskCachedImage(String url);

// 清除内存缓存
clearMemoryImageCache();

保存网络图片

saveNetworkImageToPhoto(String url, {bool useCache: true});

示例代码

以下是一个完整的示例Demo,展示了如何使用extended_image插件来加载网络图片,并实现加载状态、缩放平移、编辑等功能。

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Extended Image Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Extended Image Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  final String imageUrl = 'https://photo.tuchong.com/4870004/f/298584322.jpg';
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    )..forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ExtendedImage.network(
              imageUrl,
              width: 300,
              height: 300,
              fit: BoxFit.cover,
              cache: true,
              loadStateChanged: (ExtendedImageState state) {
                switch (state.extendedImageLoadState) {
                  case LoadState.loading:
                    return Image.asset("assets/loading.gif", fit: BoxFit.fill);
                  case LoadState.completed:
                    return FadeTransition(
                      opacity: _controller,
                      child: ExtendedRawImage(
                        image: state.extendedImageInfo?.image,
                        width: 300,
                        height: 300,
                      ),
                    );
                  case LoadState.failed:
                    return GestureDetector(
                      child: Stack(
                        fit: StackFit.expand,
                        children: [
                          Image.asset("assets/failed.jpg", fit: BoxFit.fill),
                          Positioned(
                            bottom: 0.0,
                            left: 0.0,
                            right: 0.0,
                            child: Text(
                              "load image failed, click to reload",
                              textAlign: TextAlign.center,
                            ),
                          )
                        ],
                      ),
                      onTap: () {
                        state.reLoadImage();
                      },
                    );
                }
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => FullScreenImageViewer(imageUrl: imageUrl),
                  ),
                );
              },
              child: Text('View Full Screen'),
            ),
          ],
        ),
      ),
    );
  }
}

class FullScreenImageViewer extends StatefulWidget {
  final String imageUrl;

  FullScreenImageViewer({required this.imageUrl});

  @override
  _FullScreenImageViewerState createState() => _FullScreenImageViewerState();
}

class _FullScreenImageViewerState extends State<FullScreenImageViewer> {
  final GlobalKey<ExtendedImageGestureState> _key = GlobalKey<ExtendedImageGestureState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        child: ExtendedImage.network(
          widget.imageUrl,
          fit: BoxFit.contain,
          mode: ExtendedImageMode.gesture,
          initGestureConfigHandler: (state) {
            return GestureConfig(
              minScale: 0.9,
              animationMinScale: 0.7,
              maxScale: 3.0,
              animationMaxScale: 3.5,
              speed: 1.0,
              inertialSpeed: 100.0,
              initialScale: 1.0,
              inPageView: false,
              initialAlignment: InitialAlignment.center,
            );
          },
          onDoubleTap: (ExtendedImageGestureState state) {
            var pointerDownPosition = state.pointerDownPosition;
            double begin = state.gestureDetails.totalScale;
            double end;

            _key.currentState?.animation?.removeListener(() {});
            _key.currentState?.animationController.stop();
            _key.currentState?.animationController.reset();

            if (begin == 1.0) {
              end = 2.0;
            } else {
              end = 1.0;
            }

            _key.currentState?.handleDoubleTap(
              scale: end,
              doubleTapPosition: pointerDownPosition,
            );

            _key.currentState?.animationController.forward();
          },
          heroBuilderForSlidingPage: (widget) {
            return Hero(
              tag: widget.imageUrl,
              child: widget,
            );
          },
        ),
        onTap: () {
          Navigator.pop(context);
        },
      ),
    );
  }
}

这个示例展示了如何使用extended_image插件加载网络图片,并实现了加载状态、缩放平移、全屏查看等功能。希望对你有所帮助!


更多关于Flutter图片增强功能插件extended_image的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图片增强功能插件extended_image的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,extended_image 是一个功能强大的 Flutter 插件,用于增强图片显示和处理功能。以下是如何在 Flutter 项目中使用 extended_image 的代码案例,包括一些基本的功能演示,如图片加载、缓存、点击放大等。

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加 extended_image 依赖:

dependencies:
  flutter:
    sdk: flutter
  extended_image: ^x.x.x  # 替换为最新版本号

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 extended_image

import 'package:extended_image/extended_image.dart';

3. 基本使用

以下是一个基本的使用示例,展示如何加载和显示一张图片:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Extended Image Example'),
        ),
        body: Center(
          child: ExtendedImage.network(
            'https://via.placeholder.com/600x400',
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

4. 图片点击放大

extended_image 提供了点击图片放大的功能,下面是一个示例:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Extended Image Zoom Example'),
        ),
        body: Center(
          child: ExtendedImage.network(
            'https://via.placeholder.com/600x400',
            mode: ExtendedImageMode.gesture, // 启用手势模式
            fit: BoxFit.cover,
            gestureConfig: GestureConfig(
              minScale: 1.0,
              maxScale: 3.0,
              animationDuration: const Duration(milliseconds: 300),
            ),
          ),
        ),
      ),
    );
  }
}

5. 图片缓存

extended_image 默认支持图片缓存,但你也可以自定义缓存配置。以下是一个自定义缓存的示例:

import 'package:flutter/material.dart';
import 'package:extended_image/extended_image.dart';
import 'package:extended_image_library/extended_cache_image_provider.dart'; // 用于自定义缓存

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Extended Image Cache Example'),
        ),
        body: Center(
          child: ExtendedImage.network(
            'https://via.placeholder.com/600x400',
            cache: true, // 启用缓存
            loadStateChanged: (ExtendedImageState state) {
              if (state.extendedImageLoadState == LoadState.completed) {
                // 图片加载完成后的处理
                print('Image loaded successfully');
              } else if (state.extendedImageLoadState == LoadState.failed) {
                // 图片加载失败的处理
                print('Failed to load image');
              }
            },
            imageProvider: ExtendedCacheImageProvider(
              'https://via.placeholder.com/600x400',
              defaultCacheManager: DefaultCacheManager(), // 自定义缓存管理器
            ),
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

在这个示例中,我们使用了 ExtendedCacheImageProvider 来自定义缓存配置。你需要确保已经导入了 extended_image_library 包。

6. 更多功能

extended_image 还提供了许多其他功能,如图片裁剪、滤镜、多图预览等。你可以查阅官方文档获取更多信息和高级用法。

以上代码案例展示了如何在 Flutter 项目中使用 extended_image 插件来实现图片增强功能。希望这些示例能帮助你快速上手并使用这个强大的插件。

回到顶部