Flutter development practice-picture scaling and cropping box picture cropping function
During development, you need to select a picture in the album to crop out a small piece of the picture as the target picture. For example, when uploading an avatar, after the user selects the avatar, the required picture is cut out and uploaded according to the cropping madness. Here is a record of the image scaling and cropping box image cropping function implemented using extend_image.
1. Overall effect
Image cropping can zoom the image and crop out a part of the image, or drag the cropping box to crop out the required part of the image.
As shown below
2. Use extend_image to achieve cropping
The extend_image plug-in is used here
2.1 Import the required libraries
Introduce the required libraries into the project’s pubspec.yaml
# path path path_provider: ^2.0.15 # image_editor image_editor: ^1.0.2 #extended_image extended_image: ^7.0.2
I use three components here
- path_provider
path path - image_editor
Image editing and cropping - extended_image
Image expandable, zoomable
2.2 Code Implementation
Use extended_image to display a scalable cropping box. Here you need to set the mode to ExtendedImageMode.editor.
Set initialized EditorConfig
initEditorConfigHandler: (ExtendedImageState? state) { return EditorConfig( maxScale: 4.0, cropRectPadding: const EdgeInsets.all(20.0), hitTestSize: 20.0, initCropRectType: InitCropRectType.imageRect, cropAspectRatio: CropAspectRatios.ratio4_3, editActionDetailsIsChanged: (EditActionDetails? details) { //print(details?.totalScale); }); },
The complete code is as follows
import 'dart:io'; import 'package:extended_image/extended_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_app_demolab/crop_editor_helper.dart'; import 'package:path_provider/path_provider.dart'; class ImageCropper extends StatefulWidget { const ImageCropper({super.key}); @override State<ImageCropper> createState() => _ImageCropperState(); } class _ImageCropperState extends State<ImageCropper> { final GlobalKey<ExtendedImageEditorState> editorKey = GlobalKey<ExtendedImageEditorState>(); bool _cropping = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("ImageCropper test page"), ), body: ExtendedImage.network( 'https://c-ssl.dtstatic.com/uploads/blog/202310/26/5zSdYLmWhOYYZxZ.thumb.1000_0.jpeg', fit: BoxFit.contain, mode: ExtendedImageMode.editor, enableLoadState: true, extendedImageEditorKey: editorKey, cacheRawData: true, //maxBytes: 1024 * 50, initEditorConfigHandler: (ExtendedImageState? state) { return EditorConfig( maxScale: 4.0, cropRectPadding: const EdgeInsets.all(20.0), hitTestSize: 20.0, initCropRectType: InitCropRectType.imageRect, cropAspectRatio: CropAspectRatios.ratio4_3, editActionDetailsIsChanged: (EditActionDetails? details) { //print(details?.totalScale); }); }, ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.crop), onPressed: () { cropImage(); }), ); } Future<void> cropImage() async { if (_cropping) { return; } _cropping = true; try { final Uint8List fileData = Uint8List.fromList((await cropImageDataWithNativeLibrary( state: editorKey.currentState!))!); //Save the image locally saveImage(fileData); // final String? fileFath = // await ImageSaver.save('extended_image_cropped_image.jpg', fileData); // print('save image : $fileFath'); } finally { _cropping = false; } } void saveImage(Uint8List imageByte) async { var tmpDir = await getTemporaryDirectory(); var file = await File("${tmpDir.path}/image_${DateTime.now().microsecond}.jpg").create(); file.writeAsBytesSync(imageByte); print("saveImage file:${file.path}"); int length = await file.length(); print('saveImage file length:${length}'); // Uint8List readImageData = await file.readAsBytesSync(); } }
Use image_editor to implement the image cropping function. The cropping part of Android and iOS is used here.
The specific code is as follows
import 'dart:ui'; // import 'package:isolate/load_balancer.dart'; // import 'package:isolate/isolate_runner.dart'; import 'package:extended_image/extended_image.dart'; import 'package:flutter/foundation.dart'; import 'package:image_editor/image_editor.dart'; Future<Uint8List?> cropImageDataWithNativeLibrary( {required ExtendedImageEditorState state}) async { print('native library start cropping'); Rect cropRect = state.getCropRect()!; if (state.widget.extendedImageState.imageProvider is ExtendedResizeImage) { final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List(state.rawImageData); final ImageDescriptor descriptor = await ImageDescriptor.encoded(buffer); final double widthRatio = descriptor.width / state.image!.width; final double heightRatio = descriptor.height / state.image!.height; cropRect = Rect.fromLTRB( cropRect.left * widthRatio, cropRect.top * heightRatio, cropRect.right * widthRatio, cropRect.bottom * heightRatio, ); } final EditActionDetails action = state.editAction!; final int rotateAngle = action.rotateAngle.toInt(); final bool flipHorizontal = action.flipY; final bool flipVertical = action.flipX; final Uint8List img = state.rawImageData; final ImageEditorOption option = ImageEditorOption(); if (action.needCrop) { option.addOption(ClipOption.fromRect(cropRect)); } if (action.needFlip) { option.addOption( FlipOption(horizontal: flipHorizontal, vertical: flipVertical)); } if (action.hasRotateAngle) { option.addOption(RotateOption(rotateAngle)); } final DateTime start = DateTime.now(); final Uint8List? result = await ImageEditor.editImage( image: img, imageEditorOption: option, ); print('${DateTime.now().difference(start)} :total time'); return result; }
Here you can set the edited imageEditorOption based on rotation, flip, etc.
Finally, we need to save the cropped image to a local directory, so we need to use File here.
Save the code as follows
void saveImage(Uint8List imageByte) async { var tmpDir = await getTemporaryDirectory(); var file = await File("${tmpDir.path}/image_${DateTime.now().microsecond}.jpg").create(); file.writeAsBytesSync(imageByte); print("saveImage file:${file.path}"); int length = await file.length(); print('saveImage file length:${length}'); // Uint8List readImageData = await file.readAsBytesSync(); }
If you need to read images locally, you can use file.readAsBytesSync(). Finally, Uint8List readImageData is displayed and processed.
3. Summary
Flutter development practice-picture scaling and cropping box picture cropping function
Study and record, keep improving every day.