Flutter development practice-picture scaling and cropping box picture cropping function

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.