A flutter plugin which provides Crop Widget for cropping images.

Overview

crop_your_image

A flutter plugin which provides Crop Widget for cropping images.

crop_your_image provides only minimum UI for deciding cropping area inside images. Other UI parts, such as "Crop" button or "Change Aspect Ratio" button, need to be prepared by each app developers.

This policy helps app developers to build "Cropping page" with the design of their own brand.In order to control the actions for cropping images, you can use CropController from whatever your Widgets.

Image Cropping Preview

Features

  • Minimum UI restrictions.
  • Flexible Crop widget which can be placed anywhere on your widget tree.
  • CropController to control crop actions.
  • Crop with both rect and circle
  • Fix aspect ratio.
  • Set the rect of cropping area programmatically.

Note that this package DON'T

  • read / download image data from any storages, such as gallery, internet, etc.
  • resize, tilt, or other conversions which can be done with image package directly.
  • provide UI parts other than cropping editor, such as "Crop" button, "Preview" button or "Change Aspect Ratio" menu. Building UI is completely UP TO YOU!

Note

Please note that this package is at the very starting point of developping. I'm always waiting for your feedbacks and Pull Requests for making crop_your_image more handy and useful with less bugs.

Usage

Basics

Place Crop Widget wherever you want to place image cropping UI.

final _controller = CropController();

Widget build(BuildContext context) {
  return Crop(
    image: _imageData,
    controller: _controller,
    onCropped: (image) {
      // do something with image data 
    }
  );
}

Then, Crop widget will automatically display cropping editor UI on users screen with given image.

By creating a CropController instance and pass it to controller property of Crop, you can controll the Crop widget from your own designed Widgets.

For example, when you want to crop the image with current selected cropping area, you can just call _controller.crop() wherever you want, such like the code below.

ElevatedButton(
  child: Text('Crop it!')
  onPressed: _cropController.crop,
),

Because _controller.crop() only kicks the cropping process, this method returns immediately without any cropped image data. You can always obtain the result of cropping images via onCropped callback of Crop Widget.

Advanced

All the properties of Crop and their usages are below.

final _controller = CropController();

Widget build(BuildContext context) {
  return Crop(
    image: _imageData,
    controller: _controller,
    onCropped: (image) {
      // do something with image data 
    },
    aspectRatio: 4 / 3,
    initialSize: 0.5,
    // initialArea: Rect.fromLTWH(240, 212, 800, 600),
    // withCircleUi: true,
    baseColor: Colors.blue.shade900,
    maskColor: Colors.white.withAlpha(100),
    onMoved: (newRect) {
      // do something with current cropping area.
    },
    onStatusChanged: (status) {
      // do something with current CropStatus
    }
    cornerDotBuilder: (size, edgeAlignment) => const DotControl(color: Colors.blue),
  );
}
  • image is Image data whose type is UInt8List, and the result of cropping can be obtained via onCropped callback.
  • aspectRatio is the aspect ratio of cropping area. Set null or just omit if you want to crop images with any aspect ratio.
  • aspectRatio can be changed dynamically via setter of CropController.aspectRatio. (see below)
  • initialSize is the initial size of cropping area. 1.0 (or null, by default) fits the size of image, which means cropping area extends as much as possible. 0.5 would be the half. This value is also referred when aspectRatio changes via CropController.aspectRatio.
  • initialArea is the initial Rect of cropping area based on actual image data.
  • withCircleUi flag is to decide the shape of cropping UI. If true, aspectRatio is automatically set 1.0 and the shape of cropping UI would be circle. Note that this flag does NOT affect to the result of cropping image. If you want cropped images with circle shape, call CropController.cropCircle instead of CropController.crop.
  • baseColor is the color of the mask widget which is placed over the cropping editor.
  • maskColor is the color of the base color of the cropping editor.
  • onMoved callback is called when cropping area is moved regardless of its reasons. newRect of argument is current Rect of cropping area.
  • onStatusChanged callback is called when status of Crop is changed.
  • cornerDotBuilder is the builder to build Widget placed at corners. The builder passes size which widget must follow and edgeAlignment which indicates the position.

In addition, image, aspectRatio, withCircleUi, rect and area can also be changed via CropController, and other properties, such as baseColor, maskColor and cornerDotBuilder, can be changed by setState.

Gallery App

The repository below is for a sample app of using crop_your_image.

chooyan-eng/crop_your_image_gallery

You can find several examples with executable source codes here.

Contact

If you have anything you want to inform me (@chooyan-eng), such as suggestions to enhance this package or functionalities you want etc, feel free to make issues on GitHub or send messages on Twitter @chooyan_i18n.

Comments
  • big image problem

    big image problem

    dear @chooyan-eng thank you for this good crop image i like it

    it has one problem on big image

    you can see it on this video :

    https://user-images.githubusercontent.com/15640035/112752765-d0e8c600-8fe9-11eb-9a58-a6d4ad571431.mp4

    opened by kiaxseventh 10
  • Not compatible with Flutter 1.17.5

    Not compatible with Flutter 1.17.5

    I need to use this plugin in Flutter 1.17.5.

    If i upgrade to Flutter latest version and add this plugin. Version solving is failing due to other dependencies.

    opened by Dinesh41 7
  • [PROPOSAL] Give ability to customize the loading indicator

    [PROPOSAL] Give ability to customize the loading indicator

    Hi! It will be very good to have access to change the progress indicator.

    Could you please add a new parameter to the Crop widget, so we can pass our custom progress indicator widget? Many thank in advance.

    image

    opened by PTLam25 3
  • LateError (LateInitializationError: Field '_delegate@130417691' has not been initialized.)

    LateError (LateInitializationError: Field '_delegate@130417691' has not been initialized.)

    I am facing this issue occasionally. sometimes it works and sometimes not.

    Exception has occurred.
    LateError (LateInitializationError: Field '_delegate@130417691' has not been initialized.)
    
    

    My use case -> I turned a pdf into a list of images (each page as image) and rendering each page in a Pageview widget. on each page i instantiate a crop controller and wrap the image under Crop widget . There is a button in the end which on clicking executes controller.crop() method.

    Putting screenshot here IMG_627195AD7A61-1

    Pasting the code below too if it helps.

    builder: (context, constraints) => PreloadPageView.builder(
                controller: controller.preloadPageController,
                physics: NeverScrollableScrollPhysics(),
                preloadPagesCount: 1,
                itemCount: pageCount,
                itemBuilder: (context, index) => FutureBuilder<Uint8List>(
                  future: controller.getPageImage(pdfDocument!, index + 1),
                  builder: (context, AsyncSnapshot<Uint8List> snapshot) {
                    final cropController = CropController();
                    return !snapshot.hasData
                        ? Container()
                        : SafeArea(
                            child: Column(
                              children: [
                                Expanded(
                                  child: Crop(
                                    image: snapshot.data!,
                                    controller: cropController,
                                    onCropped: (image) {
                                      controller.saveImage(image);
                                    },
                                    initialSize: 0.5,
                                  ),
                                ),
                                Row(
                                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                  children: [
                                    Row(
                                      mainAxisAlignment:
                                          MainAxisAlignment.spaceEvenly,
                                      children: [
                                        IconButton(
                                          onPressed: index == 0
                                              ? null
                                              : () {
                                                  controller.preloadPageController
                                                      .previousPage(
                                                          duration: Duration(
                                                              milliseconds: 300),
                                                          curve: Curves.easeIn);
                                                },
                                          icon: Icon(Icons.navigate_before),
                                          iconSize: 32,
                                        ),
                                        Text("${index + 1}/$pageCount"),
                                        IconButton(
                                          onPressed: index == (pageCount - 1)
                                              ? null
                                              : () {
                                                  controller.preloadPageController
                                                      .nextPage(
                                                          duration: Duration(
                                                              milliseconds: 300),
                                                          curve: Curves.easeIn);
                                                },
                                          icon: Icon(Icons.navigate_next),
                                          iconSize: 32,
                                        ),
                                      ],
                                    ),
                                    ElevatedButton.icon(
                                        onPressed: () {
                                          cropController.crop();
                                        },
                                        icon: Icon(Icons.crop),
                                        label: Text('Snap')),
                                    Padding(
                                        padding:
                                            EdgeInsets.symmetric(horizontal: 4),
                                        child: OutlinedButton(
                                          onPressed: () {
                                            Navigator.push(
                                                context,
                                                MaterialPageRoute(
                                                  builder: (context) =>
                                                      SnappedItems(),
                                                ));
                                          },
                                          child: Obx(() => Text(
                                              "Save ${controller.snappedImgs.length} Notes")),
                                        ))
                                  ],
                                )
                              ],
                            ),
                          );
                  },
                ),
    
    opened by deepu 3
  • Widget fails in WidgetTest

    Widget fails in WidgetTest

    Hi,

    I'm having a hard time writing widget tests, when I use your widget. Have you ever tried using your widget in a widget test? It seems like images are not getting loaded, no matter how long I wait in the widget test.

    Do you have any suggestions on that?

    Thanks!

    opened by NikoBoerger 2
  • Freeze UI

    Freeze UI

    The code in this line takes a very long time to execute https://github.com/chooyan-eng/crop_your_image/blob/7bf38421e5469b212e9d7a2dc574d29f8f6a4f4f/lib/src/crop.dart#L192

    I think it must be async or move to Isolate.

    enhancement 
    opened by DrobyshevAlex 2
  • added progressIndicator param

    added progressIndicator param

    Usage

    Crop(
      progressIndicator: const CircularProgressIndicator(),
    ),
    

    or

    Crop(
      progressIndicator: const Text('preparing image...'),
    ),
    

    This change will fix widget test failure.

    opened by chooyan-eng 0
  • The cropping image is too large when interactive is true

    The cropping image is too large when interactive is true

    After the value of 'interactive' was set to true, the cropping image shown on the screen turned too large. When interactive was false, the cropping image automatically fitted the width of the screen or another. I tried to reset the size of the cropping image shown on the screen. So I looked up for the parameter to resize the initial size of the cropping image, but there wasn't. What I wanted to was to resize the shown image to crop hoping you to add a new parameter function such as initialImageSize for instance.

    {ImageSize Function()? imageSize} Type: ImageSize Function()?

    ImageSize({double? width, double? height});

    Example initialImageSize: ImageSize(width, height);

    With the param, initialImageSize, the image to crop will be shown possibly to fit either of the width or the height of the screen with 'interactive' set to true.

    opened by KT99-bit 0
  • Feedback on interactive mode

    Feedback on interactive mode

    I use Crop in interactive: true, fixArea: true mode. It work great, but encountered two situations where API wasn't adequate with interactive mode:

    • No equivalent of onMove; couldn't detect when user started to interact with crop widget. Use case: I wanted to display a hint animation on usage, that should disappear when user start using crop widget.
    • In interactive crop one could want to replace the four dot overlay with something more adapted to interactive mode. The parameter to customize the 4 dots overlays, cornerDotBuilder, don't support that. I managed to synchronize initialAreaBuilder and a custom overlay on top of Crop, but it's not ideal.

    It's less an issue than a feedback, and it surmontable, but thought it was worth reporting.

    opened by gmarizy 0
  • Change of Copyright notice

    Change of Copyright notice

    Hello! According to the Appendix of Apache License 2.0, if you want to license your software under this License you should "attach the boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information". This condition is not met now. Сould you remove the copyright from the text of the license and add a COPYRIGHT NOTICE FILE (like this) in the appropriate form instead (including the year of the software development and your name and surname)? You could also apply the Apache License to your source-code files by attaching the notice to as a comment at the top of each file. Thank you in advance!

    Apache.org says: Include a copy of the Apache License, typically in a file called LICENSE, in your work, and consider also including a NOTICE file. It is also valuable to tag each of your source-code files in case they become detached from the LICENSE file. To apply the Apache License to your source-code files, one approach is to attach the following notice to as a comment at the top of each file. Replace the copyright templates with your own identifying information:

    Copyright [yyyy] [name of copyright owner]

    Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

    opened by lubovtorina 0
  • Default value of `initialSize` not working?

    Default value of `initialSize` not working?

    The doc says:

    initialSize is the initial size of cropping area. 1.0 (or null, by default) fits the size of image, which means cropping area extends as much as possible. 0.5 would be the half. This value is also referred when aspectRatio changes via CropController.aspectRatio.

    I tried setting initialSize to 1.0 or null. Neither worked. The default crop size doesn't seem to fill the image.

    image

    opened by mgenware 0
  • Aborting the crop during load leads to an error

    Aborting the crop during load leads to an error

    The cropper does a bit of async work on start up, and if the user / app dismisses the widget during that time, errors will be thrown due to context being accessed after it has been unmounted.

    This happens e.g. in https://github.com/chooyan-eng/crop_your_image/blob/c80794432d2fb5f45d541248c2b6df6adb6078d4/lib/src/crop.dart#L369

    I think all the future.then need to the mounted in the callback before doing anything else.

    E/flutter (14746): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
    E/flutter (14746): Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.
    E/flutter (14746): #0      State.context.<anonymous closure> (package:flutter/src/widgets/framework.dart:935:9)
    E/flutter (14746): #1      State.context (package:flutter/src/widgets/framework.dart:941:6)
    E/flutter (14746): #2      _CropEditorState._resetCroppingArea (package:crop_your_image/src/crop.dart:369:38)
    E/flutter (14746): #3      _CropEditorState.didChangeDependencies.<anonymous closure> (package:crop_your_image/src/crop.dart:339:9)
    E/flutter (14746): <asynchronous suspension>
    E/flutter (14746): 
    

    And from Firebase:

    CleanShot 2022-11-17 at 15 24 39@2x
    opened by tp 0
Releases(0.5.1)
Owner
Chooyan
Developing crop_your_image, a Flutter package that provides cropping images functionality. Twitter(Japanese): @chooyan_i18n
Chooyan
📸 Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget.

?? Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget. Photo View is largely used to show interacive images and other stuff such as SVG.

Blue Fire 1.7k Jan 7, 2023
A Flutter package which provides helper widgets for selecting single or multiple account/user from the given list.

account_selector A Flutter package which provides helper widgets for selecting single or multiple account/user from a list Supported Dart Versions Dar

Harpreet Singh 49 Oct 7, 2021
A flutter package which provides most commonly used widgets with their normal and neon version

This is a flutter package which provides most commonly used widgets with their normal and neon version. There are multiple different types of widgets under this package, which can be used to create more neon theme widget

ojas 24 Oct 7, 2022
A widget to provides horizontal or vertical multiple split view for Flutter.

Multi split view A widget to provides horizontal or vertical multiple split view for Flutter. Horizontal or vertical Configurable weight or size for e

Carlos Eduardo Leite de Andrade 63 Dec 28, 2022
A Flutter plugin which makes it straightforward to show the native equivalent of a CupertinoAlertDialog or CupertinoActionSheet dialog

A Flutter plugin which makes it straightforward to show the native equivalent of a CupertinoAlertDialog or CupertinoActionSheet dialog

Christoph Krassnigg 9 Dec 9, 2022
Customizable Flutter widget which syncronize ScrollView with PageView as tabs

scrollable_list_tab_scroller Customizable Flutter widget which syncronize ScrollView with PageView as tabs. Create a custom page view as tabs which sy

Railson Ferreira de Souza 4 Dec 21, 2022
Flutter Color Picker Wheel - an easy to use widget which can be heavily customized

Flutter Color Picker Wheel Flutter Color Picker Wheel is an easy to use widget which can be heavily customized. You can use the WheelColorPicker direc

Kexin Lu 35 Oct 4, 2022
Flutter package which helps you to implement Ticket Widget in your app.

✨ Ticket Widget Flutter package which helps you to implement Ticket Widget in your app. The source code is 100% Dart, and it is an updated null safe v

Mujahid 7 Dec 30, 2022
A Flutter widget which synchronize a ScrollView and a custom tab view

scrollable_list_tabview A Flutter widget which synchronize a ScrollView and a custom tab view. The main idea is to create a custom tab view synchroniz

Aswanath C K 0 Apr 12, 2022
A sliding up panel widget which can be used to show or hide content, beautiful and simple.

flutter_sliding_up_panel A sliding up panel widget which can be used to show or hide content, beautiful and simple. demo Getting Started dependencies:

null 25 Dec 12, 2022
A widget which implicitly launches a hero animation when its position changed within the same route.

local_hero A widget which implicitly launches a hero animation when its position changed within the same route. Getting started In the pubspec.yaml of

Romain Rastel 174 Jan 6, 2023
An alternative to Overlay which allows you to easily render and hit test a widget outside its parent bounds

An alternative to Overlay which allows you to easily render and hit test a widget outside its parent bounds. Based on the original idea by @shrouxm he

gskinner team 26 Dec 31, 2022
Global loading widget, which can be used through simple configuration.

load Global loading widget, which can be used through simple configuration. Pure flutter library, not use native code. It is similar to OKToast in use

Caijinglong 35 Nov 4, 2022
Build a grouped list, which support expand/collapse section and sticky headers, support use it with sliver widget.

sticky_and_expandable_list Flutter implementation of sticky headers and expandable list.Support use it in a CustomScrollView. README i18n:中文说明 Feature

tp7309 114 Nov 16, 2022
This flutter package provides an easy implementation of a Slider Button to cancel current transaction or screen

This flutter package provides an easy implementation of a Slider Button to cancel current transaction or screen

null 222 Nov 8, 2022
Flutter reaction button plugin it is fully customizable widget such as Facebook reaction button

Flutter Reaction Button Flutter button reaction it is fully customizable widget such as Facebook reaction button. Preview Demo Usage Include 'flutter_

Abdelouahed Medjoudja 174 Dec 19, 2022
Plugin to the JSON Dynamic Widget to provide named support for Ionicons

json_dynamic_widget_plugin_ionicons Table of Contents Live Example Introduction Using the Plugin Live Example Web Introduction Plugin to the JSON Dyna

null 0 May 14, 2022
A simple Flutter widget to add in the widget tree when you want to show nothing, with minimal impact on performance.

nil A simple widget to add in the widget tree when you want to show nothing, with minimal impact on performance. Why? Sometimes, according to a condit

Romain Rastel 127 Dec 22, 2022
A flutter carousel widget, support infinite scroll, and custom child widget.

carousel_slider A carousel slider widget. Features Infinite scroll Custom child widgets Auto play Supported platforms Flutter Android Flutter iOS Flut

Bart T 1 Nov 25, 2021