13

I want to allow the user to crop the Image he chose from the image picker and save it(Similar to uploading profile pic on Whatsapp). How can I do this in flutter?

Sample Image:

enter image description here

Prasanth Kanna
  • 1,961
  • 4
  • 18
  • 25

3 Answers3

9

You can use these 2 flutter libraries to achieve this,

  1. image_picker;
  2. image_cropper

The image picker use the native libraries in iOS and Android to achieve it, therefore it will delivery a good performance way.

Here is a sample available in image_picker:

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 ConfigScreen());

class ConfigScreen 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({this.title});

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

enum AppState {
  free,
  picked,
  cropped,
}

class _MyHomePageState extends State<MyHomePage> {
  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 {
    imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
    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;
    });
  }
}
Cassio Seffrin
  • 7,293
  • 1
  • 54
  • 54
  • Please let me know why the state is used. can you please explain it. Thanks in advacne – Ajay Kumar Apr 02 '20 at 12:42
  • Hi @AjayKumar, the fundamental about state is the same in a lot of reactive front end frameworks. Basically when a setState() is called, the components are updated on the screen. In particular case the ImagePicker and the ImageCropper are external native libraries therefore the entire state needs to be updated. – Cassio Seffrin Apr 02 '20 at 13:03
  • 1
    thanks for you kind reply. Your explanation make me clear. thank you – Ajay Kumar Apr 03 '20 at 09:36
  • @AjayKumar I am happy to know it! – Cassio Seffrin Apr 04 '20 at 14:24
  • Hi, how to lock aspect ratio square when crop on iOS? – Kien Vu Mar 31 '21 at 11:32
  • I cannot test it now, but try to use something like that pickImage(source: ImageSource.gallery, maxWidth: 200.0, maxHeight: 200.0); – Cassio Seffrin Mar 31 '21 at 12:51
6

The image_picker already can crop the image. You pass in the specified width and height for the image you want and the plugin actually crops the original image.

_imageFile = ImagePicker.pickImage(source: source, maxWidth: 200.0, maxHeight: 300.0);

What you are asking for is another plugin to crop images after one has been selected and that would be outside the scope of the image_picker.

I am facing something similar and you can always have the user edit the photo or video with the built in camera app until there is a plugin made for cropping images.

For cropping images on the UI level you can do something like this: https://stackoverflow.com/a/44665742/7303311

Rody Davis
  • 1,825
  • 13
  • 20
  • Shouldn't cropping after selection still be within the scope of an image picker? For example, the Image Picker in iOS allows cropping... – Koushik Ravikumar Jun 14 '18 at 07:10
  • After you select an image you get a File type back. Then you could have another plugin to handle cropping based on a file type. The Image_picker no longer has access to the file after you select it. – Rody Davis Jun 14 '18 at 17:31
0

To crop the image from image_cropper library you case just set the

aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1)

Here's the sample code:

            CroppedFile? croppedFile = await ImageCropper().cropImage(
          sourcePath: pickedImage.path,
          aspectRatio: imagePickingType == ImagePickingType.profilePic ? CropAspectRatio(ratioX: 1, ratioY: 1) :
          imagePickingType == ImagePickingType.communityWall ? CropAspectRatio(ratioX: 16, ratioY: 9) : null,

          uiSettings: [
            AndroidUiSettings(
              toolbarTitle: cropPageTitle ?? 'Edit Image',
              toolbarColor: toolbarTextColor ??
                  theme.scaffoldBackgroundColor,
              toolbarWidgetColor: toolbarBackgroundColor ??
                  theme.primaryColor,
              initAspectRatio:
              initialAspectRation ?? CropAspectRatioPreset.original,
              lockAspectRatio: (imagePickingType == ImagePickingType.profilePic) || (imagePickingType == ImagePickingType.communityWall),
              backgroundColor: theme.scaffoldBackgroundColor,
              activeControlsWidgetColor: theme.primaryColor,

            ),
            IOSUiSettings(
              title: cropPageTitle ?? 'Edit Image',
                rotateClockwiseButtonHidden: true,
                aspectRatioPickerButtonHidden: true,
                aspectRatioLockEnabled: (imagePickingType == ImagePickingType.profilePic) || (imagePickingType == ImagePickingType.communityWall),
                rotateButtonsHidden: true,
            ),
          ],
        );
        if (croppedFile != null) {
          return File(croppedFile.path);
        } else {
          File(pickedImage.path);
        }
K.Pandey
  • 135
  • 1
  • 10