Once you've captured or selected an image in android or iOS device, you may need to crop it. There are flutter packages or libraries already written to help with this. We examine these in this article.

(a). Image Cropper

A Flutter plugin for Android and iOS supports cropping images. This plugin is based on two different native libraries so it comes with different UI between these platforms.

Image Cropper doesn't manipulate images in Dart codes directly, instead, the plugin uses Platform Channel to expose Dart APIs that Flutter application can use to communicate with two very powerful native libraries (uCrop and TOCropViewController) to crop and rotate images. Because of that, all credits belong to these libraries.

uCrop - Yalantis

This project aims to provide an ultimate and flexible image cropping experience. Made in Made in Yalantis

TOCropViewController - TimOliver

TOCropViewController is an open-source UIViewController subclass to crop out sections of UIImage objects, as well as perform basic rotations. It is excellent for things like editing profile pictures, or sharing parts of a photo online. It has been designed with the iOS Photos app editor in mind, and as such, behaves in a way that should already feel familiar to users of iOS.

How to install

Depend on it:

Run this command:

With Flutter:

 $ flutter pub add image_cropper

This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get):


dependencies:
  image_cropper: ^1.4.0

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:image_cropper/image_cropper.dart';

Android

  • Add UCropActivity into your AndroidManifest.xml
<activity
    android:name="com.yalantis.ucrop.UCropActivity"
    android:screenOrientation="portrait"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>

iOS

  • No configuration required

Usage

Required parameters

  • sourcePath: the absolute path of an image file.

Optional parameters

  • maxWidth: maximum cropped image width.

  • maxHeight: maximum cropped image height.

  • aspectRatio: controls the aspect ratio of crop bounds. If this values is set, the cropper is locked and user can't change the aspect ratio of crop bounds.

  • aspectRatioPresets: controls the list of aspect ratios in the crop menu view. In Android, you can set the initialized aspect ratio when starting the cropper by setting the value of AndroidUiSettings.initAspectRatio.

  • cropStyle: controls the style of crop bounds, it can be rectangle or circle style (default is CropStyle.rectangle).

  • compressFormat: the format of result image, png or jpg (default is ImageCompressFormat.jpg).

  • compressQuality: the value [0 - 100] to control the quality of image compression.

  • androidUiSettings: controls UI customization on Android. See Android customization.

  • iosUiSettings: controls UI customization on iOS. See iOS customization.

Note

The result file is saved in NSTemporaryDirectory on iOS and application Cache directory on Android, so it can be lost later, you are responsible for storing it somewhere permanent (if needed).

Full Example

Here's an image cropping example with this library:

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';

import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ImageCropper',
      theme: ThemeData.light().copyWith(primaryColor: Colors.deepOrange),
      home: MyHomePage(
        title: 'ImageCropper',
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({required this.title});

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

enum AppState {
  free,
  picked,
  cropped,
}

class _MyHomePageState extends State<MyHomePage> {
  late AppState state;
  File? imageFile;

  @override
  void initState() {
    super.initState();
    state = AppState.free;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: imageFile != null ? Image.file(imageFile!) : Container(),
      ),
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.deepOrange,
        onPressed: () {
          if (state == AppState.free)
            _pickImage();
          else if (state == AppState.picked)
            _cropImage();
          else if (state == AppState.cropped) _clearImage();
        },
        child: _buildButtonIcon(),
      ),
    );
  }

  Widget _buildButtonIcon() {
    if (state == AppState.free)
      return Icon(Icons.add);
    else if (state == AppState.picked)
      return Icon(Icons.crop);
    else if (state == AppState.cropped)
      return Icon(Icons.clear);
    else
      return Container();
  }

  Future<Null> _pickImage() async {
    final pickedImage =
        await ImagePicker().getImage(source: ImageSource.gallery);
    imageFile = pickedImage != null ? File(pickedImage.path) : null;
    if (imageFile != null) {
      setState(() {
        state = AppState.picked;
      });
    }
  }

  Future<Null> _cropImage() async {
    File? croppedFile = await ImageCropper.cropImage(
        sourcePath: imageFile!.path,
        aspectRatioPresets: Platform.isAndroid
            ? [
                CropAspectRatioPreset.square,
                CropAspectRatioPreset.ratio3x2,
                CropAspectRatioPreset.original,
                CropAspectRatioPreset.ratio4x3,
                CropAspectRatioPreset.ratio16x9
              ]
            : [
                CropAspectRatioPreset.original,
                CropAspectRatioPreset.square,
                CropAspectRatioPreset.ratio3x2,
                CropAspectRatioPreset.ratio4x3,
                CropAspectRatioPreset.ratio5x3,
                CropAspectRatioPreset.ratio5x4,
                CropAspectRatioPreset.ratio7x5,
                CropAspectRatioPreset.ratio16x9
              ],
        androidUiSettings: AndroidUiSettings(
            toolbarTitle: 'Cropper',
            toolbarColor: Colors.deepOrange,
            toolbarWidgetColor: Colors.white,
            initAspectRatio: CropAspectRatioPreset.original,
            lockAspectRatio: false),
        iosUiSettings: IOSUiSettings(
          title: 'Cropper',
        ));
    if (croppedFile != null) {
      imageFile = croppedFile;
      setState(() {
        state = AppState.cropped;
      });
    }
  }

  void _clearImage() {
    imageFile = null;
    setState(() {
      state = AppState.free;
    });
  }
}

Reference

Find full reference here.

(b). flutter_native_image

Native Flutter Image tools

This plugin aims to have native tools to resize images and reduce their quality by compression. The code is somewhat hacky (especially the iOS part), but it works for my needs and hasn't crashed on me. Feel free to improve it if you want to.

Right now there are a few functions. Please find some examples below.

Install

Add the following lines to your pubspec.yaml under dependencies

flutter_native_image: ^0.0.6

Usage

Crop an image

File croppedFile = await FlutterNativeImage.cropImage(file.path, originX, originY, width, height);

Returns a file containing the image cropped with the given dimensions.

Other Functionalities

This library provides extra functionalities like:

Compress an image

File compressedFile = await FlutterNativeImage.compressImage(file.path,
    quality: quality, percentage: percentage);

You have to give it a file from the file system and optionally provide a quality (1-100) and a resizing percentage (1-100). Each platform will use it's proper tools to handle the resizing.

To resize the image to the certain size, use following code:

ImageProperties properties = await FlutterNativeImage.getImageProperties(file.path);
File compressedFile = await FlutterNativeImage.compressImage(file.path, quality: 80,
    targetWidth: 600, targetHeight: 300);

Keep aspect ratio of the file:

ImageProperties properties = await FlutterNativeImage.getImageProperties(file.path);
File compressedFile = await FlutterNativeImage.compressImage(file.path, quality: 80,
    targetWidth: 600,
    targetHeight: (properties.height * 600 / properties.width).round());

Get image properties

ImageProperties properties = await FlutterNativeImage.getImageProperties(file.path);

It returns an ImageProperties object containing the width and the height of the image.

Reference

Find full reference here.