[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-9264":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":22,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":31,"readmeContent":32,"aiSummary":33,"trendingCount":16,"starSnapshotCount":16,"syncStatus":34,"lastSyncTime":35,"discoverSource":36},9264,"extended_image","fluttercandies\u002Fextended_image","fluttercandies","A powerful official extension library of image, which support placeholder(loading)\u002F failed state, cache network, zoom pan image, photo view, slide out page, editor(crop,rotate,flip), paint custom etc.","https:\u002F\u002Ffluttercandies.github.io\u002Fextended_image\u002F",null,"Dart",2053,519,17,52,0,3,30.15,"MIT License",false,"master",true,[24,25,26,27,28,29,30],"cache","crop","flip","flutter","image","rotate","zoom","2026-06-12 02:02:05","# extended_image\n\n[![pub package](https:\u002F\u002Fimg.shields.io\u002Fpub\u002Fv\u002Fextended_image.svg)](https:\u002F\u002Fpub.dartlang.org\u002Fpackages\u002Fextended_image) [![GitHub stars](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002Ffluttercandies\u002Fextended_image)](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fstargazers) [![GitHub forks](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fforks\u002Ffluttercandies\u002Fextended_image)](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fnetwork) [![GitHub license](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Ffluttercandies\u002Fextended_image)](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fblob\u002Fmaster\u002FLICENSE) [![GitHub issues](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fissues\u002Ffluttercandies\u002Fextended_image)](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fissues) \u003Ca href=\"https:\u002F\u002Fqm.qq.com\u002Fq\u002FZyJbSVjfSU\">![FlutterCandies QQ 群](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdynamic\u002Fyaml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ffluttercandies%2F.github%2Frefs%2Fheads%2Fmain%2Fdata.yml&query=%24.qq_group_number&label=QQ%E7%BE%A4&logo=qq&color=1DACE8)\n\nLanguage: English| [中文简体](README-ZH.md)\n\nA powerful official extension library of images, which supports placeholder(loading)\u002F failed state, cache network, zoom pan image, photo view, slide-out page, editor (crop, rotate, flip), paint custom etc.\n\n[Web demo for ExtendedImage](https:\u002F\u002Ffluttercandies.github.io\u002Fextended_image\u002F)\n\nExtendedImage is an third-party library that extends the functionality of Flutter's Image component. The main extended features are as follows:\n\n| Feature                                                | ExtendedImage                                                      | Image                                   |\n| ------------------------------------------------------ | ------------------------------------------------------------------ | --------------------------------------- |\n| Cache network images locally and load from cache       | Supported                                                          | Not supported                           |\n| Compression display                                    | Supported, with additional options (compressionRatio and maxBytes) | Supported, with cacheHeight, cacheWidth |\n| Automatic release of image resources                   | Supported                                                          | Requires manual management              |\n| Scaling mode                                           | Supported                                                          | Not supported                           |\n| Editing mode                                           | Supported                                                          | Not supported                           |\n| Drag-to-dismiss effect for images in a page transition | Supported                                                          | Not supported                           |\n\n\n## Table of contents\n\n- [extended\\_image](#extended_image)\n  - [Table of contents](#table-of-contents)\n  - [Import](#import)\n  - [Cache Network](#cache-network)\n    - [Simple use](#simple-use)\n    - [Use ExtendedNetworkImageProvider](#use-extendednetworkimageprovider)\n  - [Load State](#load-state)\n    - [demo code](#demo-code)\n  - [Zoom Pan](#zoom-pan)\n    - [double tap animation](#double-tap-animation)\n  - [Editor](#editor)\n    - [crop aspect ratio](#crop-aspect-ratio)\n    - [crop layer painter](#crop-layer-painter)\n    - [flip, rotate, cropAspectRatio, undo ,redo , reset](#flip-rotate-cropaspectratio-undo-redo--reset)\n      - [`ImageEditorController`](#imageeditorcontroller)\n      - [flip](#flip)\n      - [rotate](#rotate)\n      - [cropAspectRatio](#cropaspectratio)\n      - [undo](#undo)\n      - [redo](#redo)\n      - [reset](#reset)\n      - [history](#history)\n      - [update config](#update-config)\n    - [crop data](#crop-data)\n      - [dart library(stable)](#dart-librarystable)\n      - [native library(faster)](#native-libraryfaster)\n  - [Photo View](#photo-view)\n  - [Slide Out Page](#slide-out-page)\n    - [enable slide out page](#enable-slide-out-page)\n    - [include your page in ExtendedImageSlidePage](#include-your-page-in-extendedimageslidepage)\n    - [make sure your page background is transparent](#make-sure-your-page-background-is-transparent)\n    - [push with transparent page route](#push-with-transparent-page-route)\n  - [Border BorderRadius Shape](#border-borderradius-shape)\n  - [Clear Save](#clear-save)\n    - [clear](#clear)\n    - [save network](#save-network)\n  - [Show Crop Image](#show-crop-image)\n  - [Paint](#paint)\n  - [Notch](#notch)\n  - [MemoryUsage](#memoryusage)\n  - [Other APIs](#other-apis)\n\n## Import\n\n*  null-safety\n\n``` yaml\nenvironment:\n  sdk: '>=2.12.0 \u003C3.0.0'\n  flutter: '>=2.0'\ndependencies:\n  extended_image: ^4.0.0\n```\n\n*  non-null-safety\n\n1.22.6 to 2.0, Flutter Api has breaking change，please use non-null-safety if you under 1.22.6.\n\n``` yaml\nenvironment:\n  sdk: '>=2.6.0 \u003C2.12.0'\n  flutter: '>1.17.0 \u003C=1.22.6'\ndependencies:\n  extended_image: ^3.0.0-non-null-safety\n```\n\n## Cache Network\n\n### Simple use\n\nYou can use `ExtendedImage.network` as Image Widget\n\n```dart\nExtendedImage.network(\n  url,\n  width: ScreenUtil.instance.setWidth(400),\n  height: ScreenUtil.instance.setWidth(400),\n  fit: BoxFit.fill,\n  cache: true,\n  border: Border.all(color: Colors.red, width: 1.0),\n  shape: boxShape,\n  borderRadius: BorderRadius.all(Radius.circular(30.0)),\n  \u002F\u002FcancelToken: cancellationToken,\n)\n```\n\n### Use ExtendedNetworkImageProvider\n\n[ExtendedNetworkImageProvider](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image_library\u002Fblob\u002Fmaster\u002Flib\u002Fsrc\u002Fextended_network_image_provider.dart)\n\n```dart\n   ExtendedNetworkImageProvider(\n      this.url, {\n      this.scale = 1.0,\n      this.headers,\n      this.cache: false,\n      this.retries = 3,\n      this.timeLimit,\n      this.timeRetry = const Duration(milliseconds: 100),\n      CancellationToken cancelToken,\n    })  : assert(url != null),\n          assert(scale != null),\n          cancelToken = cancelToken ?? CancellationToken();\n```\n\n| parameter   | description                                                                           | default             |\n| ----------- | ------------------------------------------------------------------------------------- | ------------------- |\n| url         | The URL from which the image will be fetched.                                         | required            |\n| scale       | The scale to place in the [ImageInfo] object of the image.                            | 1.0                 |\n| headers     | The HTTP headers that will be used with [HttpClient.get] to fetch image from network. | -                   |\n| cache       | whether cache image to local                                                          | false               |\n| retries     | the time to retry to request                                                          | 3                   |\n| timeLimit   | time limit to request image                                                           | -                   |\n| timeRetry   | the time duration to retry to request                                                 | milliseconds: 100   |\n| cancelToken | token to cancel network request                                                       | CancellationToken() |\n\nyou can create new provider and extends it with ExtendedProvider, and override instantiateImageCodec method.\nso that you can handle image raw data here (compress image).\n\n## Load State\n\nExtended Image provide 3 states(loading,completed,failed), you can define your state widget with\nloadStateChanged call back.\n\nloadStateChanged is not only for network, if your image need long time to load,\nyou can set enableLoadState(default value is true for network and others are false) to true\n\n![img](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002FFlutter_Candies\u002Fblob\u002Fmaster\u002Fgif\u002Fextended_image\u002Fcustom.gif)\n\nNotice:\n\n- if you don't want to override any state, please return null in this case\n\n- if you want to override size or sourceRect, you can override it with ExtendedRawImage at completed state\n\n- if you want to add something (like animation) at completed state, you can override it with ExtendedImageState.completedWidget\n\n- ExtendedImageState.completedWidget is include gesture or editor, so that you would't miss them\n\n```dart\n\u002F\u002F\u002F custom load state widget if you want\n    final LoadStateChanged loadStateChanged;\n\nenum LoadState {\n  \u002F\u002Floading\n  loading,\n  \u002F\u002Fcompleted\n  completed,\n  \u002F\u002Ffailed\n  failed\n}\n\n  \u002F\u002F\u002Fwhether has loading or failed state\n  \u002F\u002F\u002Fdefault is false\n  \u002F\u002F\u002Fbut network image is true\n  \u002F\u002F\u002Fbetter to set it's true when your image is big and take some time to ready\n  final bool enableLoadState;\n```\n\nExtendedImageState(LoadStateChanged call back)\n\n| parameter\u002Fmethod             | description                                                                                                                                   | default |\n| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| extendedImageInfo            | image info                                                                                                                                    | -       |\n| extendedImageLoadState       | LoadState(loading,completed,failed)                                                                                                           | -       |\n| returnLoadStateChangedWidget | if this is true, return widget which from LoadStateChanged function immediately(width\u002Fheight\u002Fgesture\u002Fborder\u002Fshape etc, will not effect on it) | -       |\n| imageProvider                | ImageProvider                                                                                                                                 | -       |\n| invertColors                 | invertColors                                                                                                                                  | -       |\n| imageStreamKey               | key of image                                                                                                                                  | -       |\n| reLoadImage()                | if image load failed,you can reload image by call it                                                                                          | -       |\n| completedWidget              | return completed widget include gesture or editor                                                                                             | -       |\n| loadingProgress              | return the loading progress for network image (ImageChunkEvent )                                                                              | -       |\n\n```dart\nabstract class ExtendedImageState {\n  void reLoadImage();\n  ImageInfo get extendedImageInfo;\n  LoadState get extendedImageLoadState;\n\n  \u002F\u002F\u002Freturn widget which from LoadStateChanged function immediately\n  bool returnLoadStateChangedWidget;\n\n  ImageProvider get imageProvider;\n\n  bool get invertColors;\n\n  Object get imageStreamKey;\n\n  Widget get completedWidget;\n}\n```\n\n### demo code\n\n```dart\nExtendedImage.network(\n  url,\n  width: ScreenUtil.instance.setWidth(600),\n  height: ScreenUtil.instance.setWidth(400),\n  fit: BoxFit.fill,\n  cache: true,\n  loadStateChanged: (ExtendedImageState state) {\n    switch (state.extendedImageLoadState) {\n      case LoadState.loading:\n        _controller.reset();\n        return Image.asset(\n          \"assets\u002Floading.gif\",\n          fit: BoxFit.fill,\n        );\n        break;\n\n      \u002F\u002F\u002Fif you don't want override completed widget\n      \u002F\u002F\u002Fplease return null or state.completedWidget\n      \u002F\u002Freturn null;\n      \u002F\u002Freturn state.completedWidget;\n      case LoadState.completed:\n        _controller.forward();\n        return FadeTransition(\n          opacity: _controller,\n          child: ExtendedRawImage(\n            image: state.extendedImageInfo?.image,\n            width: ScreenUtil.instance.setWidth(600),\n            height: ScreenUtil.instance.setWidth(400),\n          ),\n        );\n        break;\n      case LoadState.failed:\n        _controller.reset();\n        return GestureDetector(\n          child: Stack(\n            fit: StackFit.expand,\n            children: \u003CWidget>[\n              Image.asset(\n                \"assets\u002Ffailed.jpg\",\n                fit: BoxFit.fill,\n              ),\n              Positioned(\n                bottom: 0.0,\n                left: 0.0,\n                right: 0.0,\n                child: Text(\n                  \"load image failed, click to reload\",\n                  textAlign: TextAlign.center,\n                ),\n              )\n            ],\n          ),\n          onTap: () {\n            state.reLoadImage();\n          },\n        );\n        break;\n    }\n  },\n)\n```\n\n## Zoom Pan\n\n![img](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002FFlutter_Candies\u002Fblob\u002Fmaster\u002Fgif\u002Fextended_image\u002Fzoom.gif)\n\nExtendedImage\n\n| parameter                | description                                                                     | default |\n| ------------------------ | ------------------------------------------------------------------------------- | ------- |\n| mode                     | image mode (none, gesture, editor)                                              | none    |\n| initGestureConfigHandler | init GestureConfig when image is ready，for example, base on image width\u002Fheight | -       |\n| onDoubleTap              | call back of double tap under ExtendedImageMode.gesture                         | -       |\n| extendedImageGestureKey  | you can handle zoom\u002Fpan by using this key manually                              | -       |\n\nGestureConfig\n\n| parameter         | description                                                                                                                                                          | default                      |\n| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |\n| minScale          | min scale                                                                                                                                                            | 0.8                          |\n| animationMinScale | the min scale for zooming then animation back to minScale when scale end                                                                                             | minScale \\_ 0.8              |\n| maxScale          | max scale                                                                                                                                                            | 5.0                          |\n| animationMaxScale | the max scale for zooming then animation back to maxScale when scale end                                                                                             | maxScale \\_ 1.2              |\n| speed             | speed for zoom\u002Fpan                                                                                                                                                   | 1.0                          |\n| inertialSpeed     | inertial speed for zoom\u002Fpan                                                                                                                                          | 100                          |\n| cacheGesture      | save Gesture state (for example in ExtendedImageGesturePageView, gesture state will not change when scroll back),**remember clearGestureDetailsCache at right time** | false                        |\n| inPageView        | whether in ExtendedImageGesturePageView                                                                                                                              | false                        |\n| initialAlignment  | init image rect with alignment when initialScale > 1.0                                                                                                               | InitialAlignment.center      |\n| hitTestBehavior   | How to behave during hit tests                                                                                                                                       | HitTestBehavior.deferToChild |\n\n```dart\nExtendedImage.network(\n  imageTestUrl,\n  fit: BoxFit.contain,\n  \u002F\u002FenableLoadState: false,\n  mode: ExtendedImageMode.gesture,\n  initGestureConfigHandler: (state) {\n    return GestureConfig(\n        minScale: 0.9,\n        animationMinScale: 0.7,\n        maxScale: 3.0,\n        animationMaxScale: 3.5,\n        speed: 1.0,\n        inertialSpeed: 100.0,\n        initialScale: 1.0,\n        inPageView: false,\n        initialAlignment: InitialAlignment.center,\n        );\n  },\n)\n```\n\n### double tap animation\n\n```dart\nonDoubleTap: (ExtendedImageGestureState state) {\n  \u002F\u002F\u002Fyou can use define pointerDownPosition as you can,\n  \u002F\u002F\u002Fdefault value is double tap pointer down postion.\n  var pointerDownPosition = state.pointerDownPosition;\n  double begin = state.gestureDetails.totalScale;\n  double end;\n\n  \u002F\u002Fremove old\n  _animation?.removeListener(animationListener);\n\n  \u002F\u002Fstop pre\n  _animationController.stop();\n\n  \u002F\u002Freset to use\n  _animationController.reset();\n\n  if (begin == doubleTapScales[0]) {\n    end = doubleTapScales[1];\n  } else {\n    end = doubleTapScales[0];\n  }\n\n  animationListener = () {\n    \u002F\u002Fprint(_animation.value);\n    state.handleDoubleTap(\n        scale: _animation.value,\n        doubleTapPosition: pointerDownPosition);\n  };\n  _animation = _animationController\n      .drive(Tween\u003Cdouble>(begin: begin, end: end));\n\n  _animation.addListener(animationListener);\n\n  _animationController.forward();\n},\n```\n\n## Editor\n\n![img](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002FFlutter_Candies\u002Fblob\u002Fmaster\u002Fgif\u002Fextended_image\u002Feditor.gif)\n\n```dart\n    ExtendedImage.network(\n      imageTestUrl,\n      fit: BoxFit.contain,\n      mode: ExtendedImageMode.editor,\n      initEditorConfigHandler: (state) {\n        return EditorConfig(\n            maxScale: 8.0,\n            cropRectPadding: EdgeInsets.all(20.0),\n            hitTestSize: 20.0,\n            cropAspectRatio: _aspectRatio.aspectRatio,\n        );\n      },\n    );\n```\n\nExtendedImage\n\n| parameter               | description                                                  | default |\n| ----------------------- | ------------------------------------------------------------ | ------- |\n| mode                    | image mode (none,gestrue,editor)                             | none    |\n| initEditorConfigHandler | init EditorConfig when image is ready.                       | -       |\n| extendedImageEditorKey  | key of ExtendedImageEditorState to flip\u002Frotate\u002Fget crop rect | -       |\n\nEditorConfig\n\n| parameter              | description                                                                      | default                                                      |\n| ---------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------ |\n| maxScale               | max scale of zoom                                                                | 5.0                                                          |\n| cropRectPadding        | the padding between crop rect and image layout rect.                             | EdgeInsets.all(20.0)                                         |\n| cornerSize             | size of corner shape  (DEPRECATED! Use cornerPainter)                            | Size(30.0, 5.0)                                              |\n| cornerColor            | color of corner shape (DEPRECATED! Use cornerPainter)                            | primaryColor                                                 |\n| lineColor              | color of crop line                                                               | scaffoldBackgroundColor.withOpacity(0.7)                     |\n| lineHeight             | height of crop line                                                              | 0.6                                                          |\n| editorMaskColorHandler | call back of editor mask color base on pointerDown                               | scaffoldBackgroundColor.withOpacity(pointerDown ? 0.4 : 0.8) |\n| hitTestSize            | hit test region of corner and line                                               | 20.0                                                         |\n| animationDuration      | auto center animation duration                                                   | Duration(milliseconds: 200)                                  |\n| tickerDuration         | duration to begin auto center animation after crop rect is changed               | Duration(milliseconds: 400)                                  |\n| cropAspectRatio        | aspect ratio of crop rect                                                        | null(custom)                                                 |\n| initialCropAspectRatio | initial aspect ratio of crop rect                                                | null(custom: initial crop rect will fill the entire image)   |\n| initCropRectType       | init crop rect base on initial image rect or image layout rect                   | imageRect                                                    |\n| cornerPainter          | corner shape                                                                     | ExtendedImageCropLayerPainterNinetyDegreesCorner()           |\n| hitTestBehavior        | How to behave during hit tests                                                   | HitTestBehavior.deferToChild                                 |\n| controller             | providing functions like rotating, flipping, undoing, redoing and reset actions. | null                                                         |\n\n\n### crop aspect ratio\n\nit's a double value, so it's easy for you to define by yourself.\nfollowing are official values\n\n```dart\nclass CropAspectRatios {\n  \u002F\u002F\u002F no aspect ratio for crop\n  static const double custom = null;\n\n  \u002F\u002F\u002F the same as aspect ratio of image\n  \u002F\u002F\u002F [cropAspectRatio] is not more than 0.0, it's original\n  static const double original = 0.0;\n\n  \u002F\u002F\u002F ratio of width and height is 1 : 1\n  static const double ratio1_1 = 1.0;\n\n  \u002F\u002F\u002F ratio of width and height is 3 : 4\n  static const double ratio3_4 = 3.0 \u002F 4.0;\n\n  \u002F\u002F\u002F ratio of width and height is 4 : 3\n  static const double ratio4_3 = 4.0 \u002F 3.0;\n\n  \u002F\u002F\u002F ratio of width and height is 9 : 16\n  static const double ratio9_16 = 9.0 \u002F 16.0;\n\n  \u002F\u002F\u002F ratio of width and height is 16 : 9\n  static const double ratio16_9 = 16.0 \u002F 9.0;\n}\n```\n\n### crop layer painter\n\nyou can define your crop layer by override [EditorConfig.editorCropLayerPainter].\n\n```dart\nclass EditorCropLayerPainter {\n  const EditorCropLayerPainter();\n  void paint(\n    Canvas canvas,\n    Size size,\n    ExtendedImageCropLayerPainter painter,\n    Rect rect,\n  ) {\n    \u002F\u002F Draw the mask layer\n    paintMask(canvas, rect, painter);\n\n    \u002F\u002F Draw the grid lines\n    paintLines(canvas, size, painter);\n\n    \u002F\u002F Draw the corners of the crop area\n    paintCorners(canvas, size, painter);\n  }\n\n  \u002F\u002F\u002F draw crop layer corners\n  void paintCorners(\n      Canvas canvas, Size size, ExtendedImageCropLayerPainter painter) {\n  }\n\n  \u002F\u002F\u002F draw crop layer lines\n  void paintMask(\n      Canvas canvas, Rect rect, ExtendedImageCropLayerPainter painter) {\n  }\n  \n\n  \u002F\u002F\u002F draw crop layer lines\n  void paintLines(\n      Canvas canvas, Size size, ExtendedImageCropLayerPainter painter) {\n  } \n}\n```\n\n### flip, rotate, cropAspectRatio, undo ,redo , reset\n\n#### `ImageEditorController` \n\n```dart\nfinal ImageEditorController _editorController = ImageEditorController();\n\n    initEditorConfigHandler: (ExtendedImageState? state) {\n      return EditorConfig(\n        maxScale: 4.0,\n        cropRectPadding: const EdgeInsets.all(20.0),\n        hitTestSize: 20.0,\n        initCropRectType: InitCropRectType.imageRect,\n        cropAspectRatio: CropAspectRatios.ratio4_3,\n        controller: _editorController,\n      );\n    },\n```\n#### flip\n\n```dart\n   _editorController.flip();\n\n  void flip({\n    bool animation = false,\n    Duration duration = const Duration(milliseconds: 200),\n  })\n```\n\n\n\n #### rotate\n\n```dart\n   _editorController.rotate();\n\n  void rotate({\n    double degrees = 90,\n    bool animation = false,\n    Duration duration = const Duration(milliseconds: 200),\n    bool rotateCropRect = true,\n  })\n```\n\n\n\n #### cropAspectRatio\n\n```dart\n   _editorController.updateCropAspectRatio(CropAspectRatios.ratio4_3);\n```\n\n\n\n #### undo\n\n```dart\n  bool canUndo = _editorController.canUndo;\n   _editorController.undo();\n\n```\n\n #### redo\n\n```dart\n  bool canRedo = _editorController.canRedo;\n   _editorController.redo();\n```\n\n#### reset\n\n```dart\n   _editorController.reset();\n```\n\n\n#### history\n\n```dart\n   _editorController.currentIndex;\n   _editorController.history;\n   _editorController.saveCurrentState();\n```\n\n#### update config\n\n```dart\n   _editorController.updateConfig(EditorConfig config);\n   _editorController.config;\n```\n\n### crop data\n\n#### dart library(stable)\n\n- add [Image](https:\u002F\u002Fgithub.com\u002Fbrendan-duncan\u002Fimage) library into your pubspec.yaml, it's used to crop\u002Frotate\u002Fflip image data\n\n```yaml\ndependencies:\n  image: any\n```\n\n- get crop rect and raw image data from ExtendedImageEditorState\n\n```dart\n  \u002F\u002F\u002Fcrop rect base on raw image\n  final Rect cropRect = state.getCropRect();\n\n  var data = state.rawImageData;\n```\n\n- convert raw image data to image library data.\n\n```dart\n  \u002F\u002F\u002F it costs much time and blocks ui.\n  \u002F\u002FImage src = decodeImage(data);\n\n  \u002F\u002F\u002F it will not block ui with using isolate.\n  \u002F\u002FImage src = await compute(decodeImage, data);\n  \u002F\u002FImage src = await isolateDecodeImage(data);\n  final lb = await loadBalancer;\n  Image src = await lb.run\u003CImage, List\u003Cint>>(decodeImage, data);\n```\n\n- crop,flip,rotate data\n\n```dart\n  \u002F\u002Fclear orientation\n  image = bakeOrientation(image);\n  if (editAction.hasRotateDegrees) {\n    image = copyRotate(image, angle: editAction.rotateDegrees);\n  }\n\n  if (editAction.flipY) {\n    image = flip(image, direction: FlipDirection.horizontal);\n  }\n\n  if (editAction.needCrop) {\n    image = copyCrop(\n      image,\n      x: cropRect.left.toInt(),\n      y: cropRect.top.toInt(),\n      width: cropRect.width.toInt(),\n      height: cropRect.height.toInt(),\n    );\n  }\n```\n\n- convert to original image data\n\noutput is raw image data, you can use it to save or any other thing.\n\n```dart\n  \u002F\u002F\u002F you can encode your image\n  \u002F\u002F\u002F\n  \u002F\u002F\u002F it costs much time and blocks ui.\n  \u002F\u002Fvar fileData = encodeJpg(src);\n\n  \u002F\u002F\u002F it will not block ui with using isolate.\n  \u002F\u002Fvar fileData = await compute(encodeJpg, src);\n  \u002F\u002Fvar fileData = await isolateEncodeImage(src);\n  var fileData = await lb.run\u003CList\u003Cint>, Image>(encodeJpg, src);\n```\n\n#### native library(faster)\n\n- add [ImageEditor](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fflutter_image_editor) library into your pubspec.yaml, it's used to crop\u002Frotate\u002Fflip image data\n\n```yaml\ndependencies:\n  image_editor: any\n```\n\n- get crop rect and raw image data from ExtendedImageEditorState\n\n```dart\n  \u002F\u002F\u002Fcrop rect base on raw image\n  final Rect cropRect = state.getCropRect();\n\n  var data = state.rawImageData;\n```\n\n- prepare crop option\n\n```dart\n  if (action.hasRotateDegrees) {\n    final int rotateDegrees = action.rotateDegrees.toInt();\n    option.addOption(RotateOption(rotateDegrees));\n  }\n  if (action.flipY) {\n    option.addOption(const FlipOption(horizontal: true, vertical: false));\n  }\n\n  if (action.needCrop) {\n    Rect cropRect = imageEditorController.getCropRect()!;\n    option.addOption(ClipOption.fromRect(cropRect));\n  }\n```\n\n- crop with editImage\n\noutput is raw image data, you can use it to save or any other thing.\n\n```dart\n  final result = await ImageEditor.editImage(\n    image: img,\n    imageEditorOption: option,\n  );\n```\n\n[more detail](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fblob\u002Fmaster\u002Fexample\u002Flib\u002Fcommon\u002Futils\u002Fcrop_editor_helper.dart)\n\n## Photo View\n\nExtendedImageGesturePageView is the same as PageView and it's made for show zoom\u002Fpan image.\n\nif you have cache the gesture, remember call clearGestureDetailsCache() method at the right time.(for example,page view page is disposed)\n\n![img](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002FFlutter_Candies\u002Fblob\u002Fmaster\u002Fgif\u002Fextended_image\u002Fphoto_view.gif)\n\nExtendedImageGesturePageView\n\n| parameter    | description              | default |\n| ------------ | ------------------------ | ------- |\n| cacheGesture | whether should move page | true    |\n\nGestureConfig\n\n| parameter    | description                                                                                                                                                      | default |\n| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| cacheGesture | save Gesture state (for example in ExtendedImageGesturePageView, gesture state will not change when scroll back),remember clearGestureDetailsCache at right time | false   |\n| inPageView   | whether in ExtendedImageGesturePageView                                                                                                                          | false   |\n\n```dart\nExtendedImageGesturePageView.builder(\n  itemBuilder: (BuildContext context, int index) {\n    var item = widget.pics[index].picUrl;\n    Widget image = ExtendedImage.network(\n      item,\n      fit: BoxFit.contain,\n      mode: ExtendedImageMode.gesture,\n      gestureConfig: GestureConfig(\n        inPageView: true, initialScale: 1.0,\n        \u002F\u002Fyou can cache gesture state even though page view page change.\n        \u002F\u002Fremember call clearGestureDetailsCache() method at the right time.(for example,this page dispose)\n        cacheGesture: false\n      ),\n    );\n    image = Container(\n      child: image,\n      padding: EdgeInsets.all(5.0),\n    );\n    if (index == currentIndex) {\n      return Hero(\n        tag: item + index.toString(),\n        child: image,\n      );\n    } else {\n      return image;\n    }\n  },\n  itemCount: widget.pics.length,\n  onPageChanged: (int index) {\n    currentIndex = index;\n    rebuild.add(index);\n  },\n  controller: PageController(\n    initialPage: currentIndex,\n  ),\n  scrollDirection: Axis.horizontal,\n),\n```\n\n## Slide Out Page\n\nExtended Image support to slide out page as WeChat.\n\n![img](https:\u002F\u002Fraw.githubusercontent.com\u002Ffluttercandies\u002FFlutter_Candies\u002Fmaster\u002Fgif\u002Fextended_image\u002Fslide.gif)\n\n### enable slide out page\n\nExtendedImage\n\n| parameter                 | description                                                                                                                                      | default |\n| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------- |\n| enableSlideOutPage        | whether enable slide out page                                                                                                                    | false   |\n| heroBuilderForSlidingPage | build Hero only for sliding page, the transform of sliding page must be working on Hero,so that Hero animation wouldn't be strange when pop page | null    |\n\n### include your page in ExtendedImageSlidePage\n\ntake care of onSlidingPage call back, you can update other widgets' state as you want.\nbut, do not setState directly here, image state will changed, you should only notify the widgets which are needed to change\n\n```dart\n    return ExtendedImageSlidePage(\n      child: result,\n      slideAxis: SlideAxis.both,\n      slideType: SlideType.onlyImage,\n      onSlidingPage: (state) {\n        \u002F\u002F\u002Fyou can change other widgets' state on page as you want\n        \u002F\u002F\u002Fbase on offset\u002FisSliding etc\n        \u002F\u002Fvar offset= state.offset;\n        var showSwiper = !state.isSliding;\n        if (showSwiper != _showSwiper) {\n          \u002F\u002F do not setState directly here, the image state will change,\n          \u002F\u002F you should only notify the widgets which are needed to change\n          \u002F\u002F setState(() {\n          \u002F\u002F _showSwiper = showSwiper;\n          \u002F\u002F });\n\n          _showSwiper = showSwiper;\n          rebuildSwiper.add(_showSwiper);\n        }\n      },\n    );\n```\n\nExtendedImageGesturePage\n\n| parameter                  | description                                                                      | default                           |\n| -------------------------- | -------------------------------------------------------------------------------- | --------------------------------- |\n| child                      | The [child] contained by the ExtendedImageGesturePage.                           | -                                 |\n| slidePageBackgroundHandler | build background when slide page                                                 | defaultSlidePageBackgroundHandler |\n| slideScaleHandler          | customize scale of page when slide page                                          | defaultSlideScaleHandler          |\n| slideEndHandler            | call back of slide end,decide whether pop page                                   | defaultSlideEndHandler            |\n| slideAxis                  | axis of slide(both,horizontal,vertical)                                          | SlideAxis.both                    |\n| resetPageDuration          | reset page position when slide end(not pop page)                                 | milliseconds: 500                 |\n| slideType                  | slide whole page or only image                                                   | SlideType.onlyImage               |\n| onSlidingPage              | call back when it's sliding page, change other widgets state on page as you want | -                                 |\n| slideOffsetHandler         | customize offset when slide page                                                 | -                                 |\n\n```dart\nColor defaultSlidePageBackgroundHandler(\n    {Offset offset, Size pageSize, Color color, SlideAxis pageGestureAxis}) {\n  double opacity = 0.0;\n  if (pageGestureAxis == SlideAxis.both) {\n    opacity = offset.distance \u002F\n        (Offset(pageSize.width, pageSize.height).distance \u002F 2.0);\n  } else if (pageGestureAxis == SlideAxis.horizontal) {\n    opacity = offset.dx.abs() \u002F (pageSize.width \u002F 2.0);\n  } else if (pageGestureAxis == SlideAxis.vertical) {\n    opacity = offset.dy.abs() \u002F (pageSize.height \u002F 2.0);\n  }\n  return color.withOpacity(min(1.0, max(1.0 - opacity, 0.0)));\n}\n\nbool defaultSlideEndHandler(\n    {Offset offset, Size pageSize, SlideAxis pageGestureAxis}) {\n  if (pageGestureAxis == SlideAxis.both) {\n    return offset.distance >\n        Offset(pageSize.width, pageSize.height).distance \u002F 3.5;\n  } else if (pageGestureAxis == SlideAxis.horizontal) {\n    return offset.dx.abs() > pageSize.width \u002F 3.5;\n  } else if (pageGestureAxis == SlideAxis.vertical) {\n    return offset.dy.abs() > pageSize.height \u002F 3.5;\n  }\n  return true;\n}\n\ndouble defaultSlideScaleHandler(\n    {Offset offset, Size pageSize, SlideAxis pageGestureAxis}) {\n  double scale = 0.0;\n  if (pageGestureAxis == SlideAxis.both) {\n    scale = offset.distance \u002F Offset(pageSize.width, pageSize.height).distance;\n  } else if (pageGestureAxis == SlideAxis.horizontal) {\n    scale = offset.dx.abs() \u002F (pageSize.width \u002F 2.0);\n  } else if (pageGestureAxis == SlideAxis.vertical) {\n    scale = offset.dy.abs() \u002F (pageSize.height \u002F 2.0);\n  }\n  return max(1.0 - scale, 0.8);\n}\n```\n\n### make sure your page background is transparent\n\nif you use ExtendedImageSlidePage and slideType =SlideType.onlyImage,\nmake sure your page background is transparent\n\n### push with transparent page route\n\nyou should push page with TransparentMaterialPageRoute\u002FTransparentCupertinoPageRoute\n\n```dart\n  Navigator.push(\n    context,\n    Platform.isAndroid\n        ? TransparentMaterialPageRoute(builder: (_) => page)\n        : TransparentCupertinoPageRoute(builder: (_) => page),\n  );\n```\n\n[Slide Out Page Demo Code 1](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fblob\u002Fmaster\u002Fexample\u002Flib\u002Fcommon\u002Fwidget\u002Fcrop_image.dart)\n\n[Slide Out Page Demo Code 2](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fblob\u002Fmaster\u002Fexample\u002Flib\u002Fcommon\u002Fwidget\u002Fpic_swiper.dart)\n\n## Border BorderRadius Shape\n\nExtendedImage\n\n| parameter    | description                                                                                                                                                             | default |\n| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| border       | BoxShape.circle and BoxShape.rectangle,If this is [BoxShape.circle] then [borderRadius] is ignored.                                                                     | -       |\n| borderRadius | If non-null, the corners of this box are rounded by this [BorderRadius].,Applies only to boxes with rectangular shapes; ignored if [shape] is not [BoxShape.rectangle]. | -       |\n| shape        | BoxShape.circle and BoxShape.rectangle,If this is [BoxShape.circle] then [borderRadius] is ignored.                                                                     | -       |\n\n```dart\nExtendedImage.network(\n  url,\n  width: ScreenUtil.instance.setWidth(400),\n  height: ScreenUtil.instance.setWidth(400),\n  fit: BoxFit.fill,\n  cache: true,\n  border: Border.all(color: Colors.red, width: 1.0),\n  shape: boxShape,\n  borderRadius: BorderRadius.all(Radius.circular(30.0)),\n),\n```\n\n![img](https:\u002F\u002Fraw.githubusercontent.com\u002Ffluttercandies\u002FFlutter_Candies\u002Fmaster\u002Fgif\u002Fextended_image\u002Fimage.gif)\n\n## Clear Save\n\n### clear\n\nto clear disk cached , call clearDiskCachedImages method.\n\n```dart\n\u002F\u002F Clear the disk cache directory then return if it succeed.\n\u002F\u002F\u002F  \u003Cparam name=\"duration\">timespan to compute whether file has expired or not\u003C\u002Fparam>\nFuture\u003Cbool> clearDiskCachedImages({Duration duration})\n```\n\nto clear disk cached with specific url, call clearDiskCachedImage method.\n\n```dart\n\u002F\u002F\u002F clear the disk cache image then return if it succeed.\n\u002F\u002F\u002F  \u003Cparam name=\"url\">clear specific one\u003C\u002Fparam>\nFuture\u003Cbool> clearDiskCachedImage(String url) async {\n```\n\nget the local cached image file\n\n```dart\nFuture\u003CFile> getCachedImageFile(String url) async {\n```\n\nto clear memory cache , call clearMemoryImageCache method.\n\n```dart\n\u002F\u002F\u002Fclear all of image in memory\n clearMemoryImageCache();\n\n\u002F\u002F\u002F get ImageCache\n getMemoryImageCache() ;\n```\n\n### save network\n\ncall saveNetworkImageToPhoto and save image with [image_picker_saver](https:\u002F\u002Fgithub.com\u002Fcnhefang\u002Fimage_picker_saver)\n\n```dart\n\u002F\u002F\u002Fsave network image to photo\nFuture\u003Cbool> saveNetworkImageToPhoto(String url, {bool useCache: true}) async {\n  var data = await getNetworkImageData(url, useCache: useCache);\n  var filePath = await ImagePickerSaver.saveFile(fileData: data);\n  return filePath != null && filePath != \"\";\n}\n```\n\n## Show Crop Image\n\nget your raw image by [Load State](#Load State), and crop image by sourceRect.\n\n[ExtendedRawImage](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fblob\u002Fmaster\u002Flib\u002Fsrc\u002Fimage\u002Fraw_image.dart)\nsourceRect is which you want to show image rect.\n\n![img](https:\u002F\u002Fraw.githubusercontent.com\u002Ffluttercandies\u002FFlutter_Candies\u002Fmaster\u002Fgif\u002Fextended_image\u002Fcrop.gif)\n\n```dart\nExtendedRawImage(\n  image: image,\n  width: num400,\n  height: num300,\n  fit: BoxFit.fill,\n  sourceRect: Rect.fromLTWH(\n      (image.width - width) \u002F 2.0, 0.0, width, image.height.toDouble()),\n)\n```\n\n## Paint\n\nprovide BeforePaintImage and AfterPaintImage callback, you will have the chance to paint things you want.\n\n![img](https:\u002F\u002Fraw.githubusercontent.com\u002Ffluttercandies\u002FFlutter_Candies\u002Fmaster\u002Fgif\u002Fextended_image\u002Fpaint.gif)\n\nExtendedImage\n\n| parameter        | description                                            | default |\n| ---------------- | ------------------------------------------------------ | ------- |\n| beforePaintImage | you can paint anything if you want before paint image. | -       |\n| afterPaintImage  | you can paint anything if you want after paint image.  | -       |\n\n```dart\n  ExtendedImage.network(\n    url,\n    width: ScreenUtil.instance.setWidth(400),\n    height: ScreenUtil.instance.setWidth(400),\n    fit: BoxFit.fill,\n    cache: true,\n    beforePaintImage: (Canvas canvas, Rect rect, ui.Image image) {\n      if (paintType == PaintType.ClipHeart) {\n        if (!rect.isEmpty) {\n          canvas.save();\n          canvas.clipPath(clipheart(rect, canvas));\n        }\n      }\n      return false;\n    },\n    afterPaintImage: (Canvas canvas, Rect rect, ui.Image image) {\n      if (paintType == PaintType.ClipHeart) {\n        if (!rect.isEmpty) canvas.restore();\n      } else if (paintType == PaintType.PaintHeart) {\n        canvas.drawPath(\n            clipheart(rect, canvas),\n            Paint()\n              ..colorFilter =\n                  ColorFilter.mode(Color(0x55ea5504), BlendMode.srcIn)\n              ..isAntiAlias = false\n              ..filterQuality = FilterQuality.low);\n      }\n    },\n  );\n```\n\nsee [paint image demo](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fblob\u002Fmaster\u002Fexample\u002Flib\u002Fpages\u002Fsimple\u002Fpaint_image_demo.dart)\nand [push to refresh header which is used in crop image demo](https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fblob\u002Fmaster\u002Fexample\u002Flib\u002Fcommon\u002Fwidget\u002Fpush_to_refresh_header.dart)\n\n## Notch\n\nBy setting layoutInsets, you can ensure the image is positioned outside of obstructing elements such as\nthe phone notch or home indicator if displayed in full screen. This will still allow the image margin to\nshow underneath the notch if zoomed in. \n\nExtendedImage\n\n| parameter    | description                                       | default         |\n| ------------ | ------------------------------------------------- | --------------- |\n| layoutInsets | Amount to inset from the edge during image layout | EdgeInsets.zero |\n\n```dart\n  ExtendedImage.network(\n    url,\n    fit: BoxFit.contain,\n    layoutInsets: MediaQuery.of(context).padding\n  );\n```\n\n## MemoryUsage\n\nYou can reduce memory usage with following settings now.\n\n* ExtendedResizeImage\n\n| parameter                                                | description                                                                                                                                                                   | default  |\n| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |\n| [ExtendedResizeImage.compressionRatio]                   | The image`s size will resize to original * [compressionRatio].It's ExtendedResizeImage`s first pick.The compressionRatio`s range is from 0.0 (exclusive), to 1.0 (exclusive). | null     |\n| [ExtendedResizeImage.maxBytes]                           | [ExtendedResizeImage] will compress the image to a size that is smaller than [maxBytes]. The default size is 50KB. It's actual bytes of Image, not decode bytes               | 50 \u003C\u003C 10 |\n| [ExtendedResizeImage.width]\u002F[ExtendedResizeImage.height] | The width\u002Fheight the image should decode to and cache. It's same as [ResizeImage],                                                                                            | null     |\n\n```dart\n    ExtendedImage.network(\n      'imageUrl',  \n      compressionRatio: 0.1,\n      maxBytes: null,\n      cacheWidth: null,\n      cacheHeight: null,  \n    )\n\n    ExtendedImage(\n      image: ExtendedResizeImage(\n        ExtendedNetworkImageProvider(\n          'imageUrl',  \n        ),\n        compressionRatio: 0.1,\n        maxBytes: null,\n        width: null,\n        height: null,\n      ),\n    )\n```\n\n* clearMemoryCacheWhenDispose\n\n| parameter                   | description                                                                                                                                                                                                                                 | default |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| clearMemoryCacheWhenDispose | It's not good enough after Flutter 2.0, it seems that we can't release memory usage if this image is not completed(https:\u002F\u002Fgithub.com\u002Ffluttercandies\u002Fextended_image\u002Fissues\u002F317). It will release memory usage only for completed image now. | false   |\n\n```dart\n   ExtendedImage.network(\n     'imageUrl',     \n     clearMemoryCacheWhenDispose: true,\n   )\n```\n\n* imageCacheName\n\n| parameter      | description                                                                                                                                               | default |\n| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| imageCacheName | The name of [ImageCache], you can define custom [ImageCache] to store this image. In this way you can work with them without affecting other [ImageCache] | null    |\n\n```dart\n   ExtendedImage.network(\n     'imageUrl',  \n     imageCacheName: 'MemoryUsage',\n   )\n     \n  \u002F\u002F\u002F clear when this page is disposed   \n  @override\n  void dispose() {\n    \u002F\u002F clear ImageCache which named 'MemoryUsage'\n    clearMemoryImageCache(imageCacheName);\n    super.dispose();\n  }   \n```\n\n## Other APIs\n\nExtendedImage\n\n| parameter                   | description                                                                                    | default |\n| --------------------------- | ---------------------------------------------------------------------------------------------- | ------- |\n| enableMemoryCache           | whether cache in PaintingBinding.instance.imageCache)                                          | true    |\n| clearMemoryCacheIfFailed    | when failed to load image, whether clear memory cache.if true, image will reload in next time. | true    |\n| clearMemoryCacheWhenDispose | when image is removed from the tree permanently, whether clear memory cache.                   | false   |\n","ExtendedImage 是一个强大的 Flutter 图像扩展库，支持加载占位图、网络图片缓存、缩放平移、照片查看、滑动页面切换以及编辑（裁剪、旋转、翻转）等功能。它基于 Dart 语言开发，能够显著增强 Flutter 应用中的图像处理能力。相比原生的 Image 组件，ExtendedImage 提供了更多高级特性，如本地缓存网络图片、自定义压缩显示选项、自动释放图像资源等，使得开发者可以更高效地管理和展示应用中的图像内容。该库适用于需要复杂图像处理功能的场景，例如社交应用的照片编辑、电商应用的商品图片展示等。",2,"2026-06-11 03:21:55","top_language"]