Giter Club home page Giter Club logo

flutter_crop's Introduction

pub package

A Flutter package for cropping any widget, not only images. This package is entirely written in Dart and supports Android, iOS, Web and Desktop. Also, because of being independent from native platform, it does not increase size of your apps output (e.g. apk).

Supported platforms

  • Flutter Android
  • Flutter iOS
  • Flutter Web
  • Flutter Desktop

Demo

Web Demo | Install from Google Play

Donation

If you find this project useful, please support me by buying me a pizza πŸ•.

Tron Address:

TLtrEU4KT2bn5J87VWfs1QDrmB1aFQ1bja

Ethereum Address:

0xf8Da77e7BbE39be8c9e527289465Bf7219af58db

I do not accept Bitcoin due to its issues with sustainability and global warming.

Getting Started

In your pubspec.yaml file add:

dependencies:
  crop: any

Then, in your code import:

import 'package:crop/crop.dart';

Now in build function, put a Crop widget in the widget tree and you are done. Please don't forget to check /example folder, there is much more.

flutter_crop's People

Contributors

frezyx avatar ipcjs avatar mirzammoin9183 avatar sanjul avatar taboosun avatar xclud avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flutter_crop's Issues

Crop issues under Flutter web (Android is fine)

  1. How do I zoom in/out in Flutter web?

  2. Why do the crop window always return to the image's center after I try to pan it?

  3. I compiled the crop demo, but the image inside the crop window is very dark (see below). The online demo is working fine.

image

  1. The oval shape does not show in Flutter web.

Image is cut

I've expected this plugin to work like the cropping function on e.g. instagram or other photo apps.

Let me give you an example:
The app expects a certain format for phots, let's say a ascpect ration of 16:9. Now the user selects a portrait photo from his gallery. The cropping tool now allow you to select a part of the image which has to fit in the 16:9 frame.

When trying the crop demo I got another behavior: When starting the demo I can see the whole sample picture. When changing the aspect ration to 16:9 the top and bottom of the image is cut off. I'm not able to select the missing part of the picture anymore.

Is that the intended behaviour or is it a bug? If it is not clear what I mean I can provide screenshots.

Cut in the top position

Apparently there is a bug that does not allow me to select an upper or lower part of the initial cut. I'm using the sample code.
As you can see in the video, I can't cut in the red or green circle, only yellow. Do you have a solution or is it a bug?

demo.zip

Image.network() support?

I'm getting an error for Image.network('..')

The cause of this appears to be --web-renderer auto (flutter run --web-renderer auto)

Removing the flag and just using flutter run resolves above but now gives Error: Unsupported operation: toImage is not supported on the Web when "crop" button is clicked from the example.

Crop(
                controller: controller,
                shape: shape,
                child: Image.network(
                  'https://example.com/f8133f38f44.png',
                  fit: BoxFit.cover,
                ),
...
)

Error:

Image provider:
NetworkImage("https://example.com/f8133f38f44.png",
scale: 1)
Image key:
NetworkImage("https://example.com/f8133f38f44.png",
scale: 1)

flutter doctor -v
[βœ“] Flutter (Channel beta, 1.25.0-8.3.pre, on Mac OS X 10.15.7 19H114 darwin-x64, locale en-GB)
β€’ Flutter version 1.25.0-8.3.pre at /Users/me/flutter
β€’ Framework revision 5d36f2e7f5 (3 weeks ago), 2021-01-14 15:57:49 -0800
β€’ Engine revision 7a8f8ca02c
β€’ Dart version 2.12.0 (build 2.12.0-133.7.beta)

[βœ“] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
β€’ Android SDK at /Users/me/Library/Android/sdk
β€’ Platform android-30, build-tools 28.0.3
β€’ Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS
β€’ Xcode at /Applications/Xcode.app/Contents/Developer
β€’ Xcode 12.2, Build version 12B45b
β€’ CocoaPods version 1.10.1

[βœ“] Chrome - develop for the web
β€’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[βœ“] Android Studio (version 4.1)
β€’ Android Studio at /Applications/Android Studio.app/Contents
β€’ Flutter plugin can be installed from:
πŸ”¨ https://plugins.jetbrains.com/plugin/9212-flutter
β€’ Dart plugin can be installed from:
πŸ”¨ https://plugins.jetbrains.com/plugin/6351-dart
β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)

[βœ“] IntelliJ IDEA Ultimate Edition (version 2020.1.1)
β€’ IntelliJ at /Applications/IntelliJ IDEA.app
β€’ Flutter plugin version 53.0.1
β€’ Dart plugin version 201.7223.99

[βœ“] Connected device (1 available)
β€’ Chrome (web) β€’ chrome β€’ web-javascript β€’ Google Chrome 88.0.4324.96

β€’ No issues found!

Rotation by 180 degrees does not work

I have been playing with the library and I found an issue when trying to rotate images by 180 degrees or higher. It seems that the library is not handling it correctly. When I try to rotate by 180, the image is placed with 0 rotation (the original position). I tried to debug the code, but I don't have enough knowledge of image manipulation algorithms.

Thanks!

Save Image to temporal directory?

I know you provided a way to save image to "gallery" and this function returns a strange path?

Future<dynamic> _saveScreenShot(ui.Image img) async {
    var byteData = await img.toByteData(format: ui.ImageByteFormat.png);
    var buffer = byteData.buffer.asUint8List();
    final result = await ImageGallerySaver.saveImage(buffer);
    print(result);

    return result;
  }

this path is pretty weird as I can't reference it.
file:///storage/emulated/0/H%20Y%20L/1588915650844.png
If I do File(imgPath) The file is invalid, well file can't be loaded or opened, etc.

is there a way to save it into getTemporaryDirectory() and give it a fixed named?

How to crop the original image.

I refer to the demo code. After loading an image, the image will be automatically cropped to a scale. But I don't want to do this, I want to be able to slide through the original image, zoom in and out, and so on.

Can it be realized? This function is urgently needed.

Support "fit to min" in addition to "fit to max".

This widget is awesome! We would like a configuration option to limit scaling to fit the min dimension. The current behavior limits scaling to the max dimension.

When cropping circular (square) avatars from rectangular source images, we would like to limit the scaling fit to the min dimension such that no parts of the resulting image are blank. The current behavior limits the scaling fit to the max dimension which can result in blank areas on the min dimension sides.

For example a rectangular vertical (portrait) image cropped to a square aspect ratio has blank area on left and right sides (i.e. min dimension). The requested option would not allow the image to be scaled smaller than what fits across the min (e.g. horizontal) dimension. Currently the scaling is limited to the max dimension (in this example the vertical dimension) which can result in blank areas on the left and/or right sides.

Total width and height

Hi. Can I set the total output width and height of my Image?
I would like to get Image (example) in 800x800 size independent from the image inside.
Too if the image inside is smaller than 800x800.

6,8 edge crop

I am extensively using this package and i need a 6 or 8 edge cropping instead of the 4.
Kindly help

How to achieve the instagram crop functionality?

Hi, first of all, I wanted to thank you for this awesome package!

I am trying to recreate the cropping from Instagram and I am a bit lost, to be honest.

This is my current screen:
1

Currently, the aspect ratio is set to 9/16 but this obviously isn't what I need.

The screen currently looks like this:

Scaffold(
   body: Column( 
       Crop(),
       GridView(),
    )
)

My questions are:

  1. How do I determine the correct aspect ratio to get boundaries like the Instagram cropper?
  2. How would I implement the button that sets the size of the media to full width?

Edit: I would also need to reset the x value of Offset to 0 after an element is dragged. Is there an easy way to do it or do I have to fork the package to do that?

Thanks!

Circle crop preview

Would be really nice to specify the preview shape of the clipper that controls the dim background.
Something like: clipper: Clippers.Oval or clipper: Clippers.Box
I've been adding a second widget as an overlay and setting the dim color to transparent as a work around.

Padding parameter not working.

It seems to me that the padding parameter of the Crop class has no effect.
What was the expected effect of this padding?

Can not change crop size

Hi, first of all thanks for all the effort you put in to create such a great library that makes cropping image in flutter web possible. In our project we want to use this library but we need to change the size of the cropper and the cropper shouldn't extend image's size (to avoid getting blank area in the cropped image).

We want pretty much the same thing as you can see in this link: https://alyle.io/components/image-cropper.

Thanks!

Not working on web + missing dependecies

There are some packages that I need to add to .yaml that are not mentioned in the documentation.
One of the is image_gallery_saver that's not supported on web.

I get this error:
Error: Unsupported operation: toImage is not supported on the Web.

Crop view doesnt let me crop at the edges with certain aspect ratio

I cant seem to get the plugin to work.
Here is my code

class CustomImageEditor extends StatefulWidget {
  final File data;

  CustomImageEditor({
    Key? key,
    required this.data,
  }) : super(key: key);

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

class _CustomImageEditorState extends State<CustomImageEditor> {
  
  late CropController _controller;

  @override
  void initState() {
    _controller = CropController(aspectRatio: 1);

    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Crop(
      onChanged: (decomposition) {},
      controller: _controller,
      child: Image.file(
        widget.data,
        fit: BoxFit.cover,
      ),
      helper: IgnorePointer(
        child: DecoratedBox(
          decoration: BoxDecoration(border: Border.all(color: Colors.white)),
        ),
      ),
    );
  }
}

This is wrapped in an aspect ratio of 1 under a column.

Here is the video to show the issue

Problem with transition in some cases

I have problem with transitions when crop section has ratio aspect vertically (like 0.5625) and image is horizontally.

This video shows problem.
https://vimeo.com/490743586

Code:

    final controller =
        useMemoized(() => CropController(aspectRatio: width / height));
Crop(
              controller: controller,
              shape: BoxShape.rectangle,
              child: Image.memory(
                bytesFile,
                fit: BoxFit.cover,
              ),
              helper: Container(
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.white, width: 2),
                ),
              ),
            )

multiple crop instances on same page

Hello!

Thanks for such a great package, working great so far.

I have though an issue with implementing multiple crop images on one view. I have three thumbnails, clicking on each display bigger image that can be cropped. I am creating three separate instances CropController and Crop widget, and switching between them.

Issue is, when user adjust picture, zoom it and drag somewhere. This works, but after switching to another image (so changing CropController and Crop widget), and then changing back to our modified picture image is zoomed as it was done by user, but its always centered, so drag part is forgotten. But! it's not really forgotten, as if user tap the screen, image jump immediately to that place where drag was done on the first place. Flow is on attached gif.

Do you have any idea how could I restore user "drag" position when switching images? Thanks in advance!

issue
issue

Zrzut ekranu 2020-11-9 o 11 02 41

Image is immediately cropped to aspect ratio

Whenever I get the image and place it as the child of Crop, it is immediately cropped to the aspect ratio passed to the controller. How do i get the initial image to maintain its original aspect ratio while still cropping it to another aspect ratio in the crop view?

Rotating then zooming out

I'm trying to crop within a 4x3 aspect ratio.

I have an image starting in landscape mode, I rotate it 90 degrees, it then doesn't let me zoom out even though I have image spillover across every direction (top, bottom, left and right). It seems to be fixed at a certain zoom level.

I pretty much pulled the demo code from the example with some edits (passing a image path over to the stful widget):

import 'package:balln/const.dart';
import 'package:balln/utils/styled/style_scaffold.dart';
import 'package:crop/crop.dart';
import 'package:flutter/material.dart';

class Cropp extends StatefulWidget {
  final String path;

  Cropp(this.path);

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

class _CroppState extends State<Cropp> {
  final controller = CropController(aspectRatio: 3 / 4);
  double _rotation = 0;
  BoxShape shape = BoxShape.rectangle;

  void _cropImage() async {
    final pixelRatio = MediaQuery.of(context).devicePixelRatio;
    final cropped = await controller.crop(pixelRatio: pixelRatio);
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return StyleScaffold(
      appBar: AppBar(
        title: Text('Crop Image'),
        backgroundColor: appBarBackground,
        // centerTitle: true,
        actions: <Widget>[
          IconButton(
            onPressed: _cropImage,
            tooltip: 'Crop',
            icon: Icon(Icons.crop),
          )
        ],
      ),
      body: Column(
        children: <Widget>[
          Expanded(
            child: Container(
              color: Colors.black,
              padding: EdgeInsets.all(8),
              child: Crop(
                onChanged: (decomposition) {
                  print(
                      "Scale : ${decomposition.scale}, Rotation: ${decomposition.rotation}, translation: ${decomposition.translation}");
                },
                controller: controller,
                shape: shape,
                child: Image.asset(widget.path,
                  fit: BoxFit.cover,
                ),
                /* It's very important to set `fit: BoxFit.cover`.
                   Do NOT remove this line.
                   There are a lot of issues on github repo by people who remove this line and their image is not shown correctly.
                */
                helper: shape == BoxShape.rectangle
                    ? Container(
                        decoration: BoxDecoration(
                          border: Border.all(color: Colors.white, width: 2),
                        ),
                      )
                    : null,
              ),
            ),
          ),
          Padding(
            padding: EdgeInsets.only(bottom: 0),
            child: Row(
              children: <Widget>[
                IconButton(
                  icon: Icon(Icons.undo),
                  tooltip: 'Undo',
                  onPressed: () {
                    controller.rotation = 0;
                    controller.scale = 1;
                    controller.offset = Offset.zero;
                    setState(() {
                      _rotation = 0;
                    });
                  },
                ),
                Expanded(
                  child: SliderTheme(
                    data: theme.sliderTheme.copyWith(
                      trackShape: RectangularSliderTrackShape(),
                    ),
                    child: Slider(
                      divisions: 8,
                      value: _rotation,
                      min: -180,
                      max: 180,
                      label: '$_rotationΒ°',
                      onChanged: (n) {
                        setState(() {
                          _rotation = n.roundToDouble();
                          controller.rotation = _rotation;
                        });
                      },
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
```1

Please add some more documentation

Hi,

your Cropping widget looks awesome and exactly what I need. Unfortunately I'm absolutely not clear on how I really have to use it.

  • What is the helper good for?
  • How is the intended workflow with this widget?
  • Do I have to handle gestures myself and use the controller accordingly to zoom / pan?
  • If I want to cut a smaller area from my image, do I have to zoom in and pan so that the area that I want fills the whole Crop widget?

I really would be greateful for some pointers.

Aspect ratio from within the image boundaries.

Hi, great little plugin. I am trying to use it to help users of an app upload their avatar. The avatar has to be square in shape. Is it possible to ensure that the user can't pinch the image below the square aspect ratio just like in the photo below - borrowing @SergeShkurko photo.

2020-03-03_15h52_34

Thanks.

Unsupported operation: toImage is not supported on the Web

I was testing the web version of the app, and when i click "Crop", i get the following error

Uncaught (in promise) Error: Unsupported operation: toImage is not supported on the Web at Object.throw_ [as throw] (errors.dart:212) at _engine.SurfaceScene.new.toImage (scene.dart:20) at layer$.OffsetLayer.new.toImage (layer.dart:1208) at toImage.next (<anonymous>) at runBody (async_patch.dart:84) at Object._async [as async] (async_patch.dart:123) at layer$.OffsetLayer.new.toImage (layer.dart:1192) at proxy_box.RenderRepaintBoundary.new.toImage (proxy_box.dart:2960) at ui$.CropController.new.crop (ui.dart:391) at main._MyHomePageState.new._cropImage (main.dart:37) at _cropImage.next (<anonymous>) at runBody (async_patch.dart:84) at Object._async [as async] (async_patch.dart:123) at main._MyHomePageState.new.[_cropImage] (main.dart:35) at ink_well._InkResponseState.new.[_handleTap] (ink_well.dart:993) at ink_well.dart:1111 at tap.TapGestureRecognizer.new.invokeCallback (recognizer.dart:183) at tap.TapGestureRecognizer.new.handleTapUp (tap.dart:598) at tap.TapGestureRecognizer.new.[_checkUp] (tap.dart:287) at tap.TapGestureRecognizer.new.acceptGesture (tap.dart:259) at arena.GestureArenaManager.new.sweep (arena.dart:157) at binding$5.WidgetsFlutterBinding.new.handleEvent (binding.dart:362) at binding$5.WidgetsFlutterBinding.new.dispatchEvent (binding.dart:338) at binding$5.WidgetsFlutterBinding.new.dispatchEvent (binding.dart:267) at binding$5.WidgetsFlutterBinding.new.[_handlePointerEvent] (binding.dart:295) at binding$5.WidgetsFlutterBinding.new.[_flushPointerEventQueue] (binding.dart:240) at binding$5.WidgetsFlutterBinding.new.[_handlePointerDataPacket] (binding.dart:213) at Object._invoke1 (window.dart:773) at _engine.EngineWindow.new.invokeOnPointerDataPacket (window.dart:374) at _engine.PointerBinding.__.[_onPointerData] (pointer_binding.dart:129) at pointer_binding.dart:479 at pointer_binding.dart:440 at pointer_binding.dart:210

Another box decoration, just an idea

Not a PR, I just copy it there, it's simple:

class CropDecoration extends Decoration {
  final Color color;
  final double cornerSize;
  final double thinWidth;
  final double thickWidth;

  CropDecoration({this.color = Colors.white, this.cornerSize = 50, this.thinWidth = 3, this.thickWidth = 6});

  @override
  BoxPainter createBoxPainter([VoidCallback onChanged]) => _CropDecorationPainter(
        color: color,
        cornerSize: cornerSize,
        thinWidth: thinWidth,
        thickWidth: thickWidth,
      );
}

class _CropDecorationPainter extends BoxPainter {
  final Color color;
  final double cornerSize;
  final double thinWidth;
  final double thickWidth;

  _CropDecorationPainter({this.color, this.cornerSize, this.thinWidth, this.thickWidth});

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
    final Rect bounds = offset & configuration.size;
    final Paint thickPaint = Paint()
      ..color = color
      ..style = PaintingStyle.stroke
      ..strokeWidth = thickWidth
      ..strokeCap = StrokeCap.round
      ..strokeJoin = StrokeJoin.miter;

    final thickPath = Path();
    thickPath.addPolygon([bounds.topLeft.translate(0, cornerSize), bounds.topLeft, bounds.topLeft.translate(cornerSize, 0)], false);
    thickPath.addPolygon([bounds.topRight.translate(0, cornerSize), bounds.topRight, bounds.topRight.translate(-cornerSize, 0)], false);
    thickPath.addPolygon([bounds.bottomLeft.translate(0, -cornerSize), bounds.bottomLeft, bounds.bottomLeft.translate(cornerSize, 0)], false);
    thickPath.addPolygon([bounds.bottomRight.translate(0, -cornerSize), bounds.bottomRight, bounds.bottomRight.translate(-cornerSize, 0)], false);
    canvas.drawPath(thickPath, thickPaint);

    final Paint thinPaint = Paint()
      ..color = color
      ..style = PaintingStyle.stroke
      ..strokeWidth = thinWidth
      ..strokeCap = StrokeCap.round
      ..strokeJoin = StrokeJoin.miter;
    final thinPath = Path();
    thinPath.addPolygon([bounds.topLeft.translate(cornerSize, 0), bounds.topRight.translate(-cornerSize, 0)], false);
    thinPath.addPolygon([bounds.bottomLeft.translate(cornerSize, 0), bounds.bottomRight.translate(-cornerSize, 0)], false);
    thinPath.addPolygon([bounds.topLeft.translate(0, cornerSize), bounds.bottomLeft.translate(0, -cornerSize)], false);
    thinPath.addPolygon([bounds.topRight.translate(0, cornerSize), bounds.bottomRight.translate(0, -cornerSize)], false);
    canvas.drawPath(thinPath, thinPaint);
  }
}

Pre-Cropping

We are using your widget to create square avatars. When we do so, it pre-crops the original image.

Here is an image.

Given this original image, we placed it inside your example app, changing only line 106 to reference this image (also adding it as an asset in the pubspec.yaml).

When we run the app and set a 1:1 aspect ratio, which is the functionality we want, here's what we end up with. We can't see the person of interest because of the pre-crop that occurs due to the fit: BoxFit.cover.

Below is the desired result.

[web/mobile] Unsupported operation: toImage is not supported on the Web

Only on web mobile (chrome and safari) am I getting "Unsupported operation: toImage is not supported on the Web", when I try to crop.

This works fine on chrome for my pc, which is interesting.

Any chance on how soon this can be fixed? Love your package so far works great besides this.

Unexpected behavior when cropping

First thank you for this simple and great package, we've been looking for a package that's easy to customize and supports desktop and web.

While I was implementing the crop functionality I noticed a strange behavior and I don't think it was designed to be used in this way. I't not easy to explain so I've posted a video to show the problem. In short, the crop area does not include the parts of the image that are not within the provided aspect ratio. For example, when setting the aspect ratio to 1.0, you can't move the image outside the square. You can however rotate or scale this area.

Example Video

I've tested this on a physical Android device using the app on Google Play. I've also tested it on macOS using latest versions of Flutter and flutter_crop [0.4.0].

Doctor:

[βœ“] Flutter (Channel dev, 1.20.0-1.0.pre, on Mac OS X 10.15.5 19F101, locale en-SA)
 
[βœ“] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
[βœ“] Xcode - develop for iOS and macOS (Xcode 11.5)
[βœ“] Chrome - develop for the web
[βœ“] Android Studio (version 4.0)
[βœ“] VS Code (version 1.46.1)
[βœ“] Connected device (3 available)

β€’ No issues found!

Thanks again.

Cropping and zooming doesn't work on Web

  1. The image looks darkened then it has;
  2. Can't zoom image(trying with the mouse wheel);
  3. Got the error: "Unsupported operation: toImage is not supported on the Web" after pressing "Crop" button.

Tested on: Chrome(Version 80.0.3987.163 (Official Build) (64-bit)), Mozilla Firefox(75.0 (64-bit))
deps:

crop: ^0.3.1+1
image_picker_web: ^1.0.6
image_picker: ^0.6.5

Source code:

import 'package:flutter/material.dart';
import 'package:image_picker_web/image_picker_web.dart';
import './image.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:ui';
import './centered_slider_track_shape.dart';
import 'package:flutter/material.dart';
import 'package:crop/crop.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:path/path.dart' as Path;
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:math';


class UserPage extends StatefulWidget {
  @override
  _UserPageState createState() => _UserPageState();
}

class _UserPageState extends State<UserPage> {
  Image pickedImage;

  @override
  void initState() {
    super.initState();
  }

  Future getImageMobile() async {
    var image = await ImagePicker.pickImage(source: ImageSource.gallery);

    setState(() {
      pickedImage = Image.file(
        image,
        fit: BoxFit.cover,
      );
    });
  }

  getImageWeb() async {
    Image fromPicker =
        await ImagePickerWeb.getImage(outputType: ImageType.widget);
    if (fromPicker != null) {
      setState(() {
        pickedImage = fromPicker;
      });
    }
  }

  pickImage() async {
    if (kIsWeb) {
      await getImageWeb();
    } else {
      await getImageMobile();
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Image Picker Web Example'),
        ),
        body: Center(
            child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  AnimatedSwitcher(
                    duration: Duration(milliseconds: 300),
                    switchInCurve: Curves.easeIn,
                    child: SizedBox(
                          width: 200,
                          child: pickedImage,
                        ) ??
                        Container(),
                  ),
                  SizedBox(
                    width: 15,
                  ),
                ],
              ),
              ButtonBar(alignment: MainAxisAlignment.center, children: <Widget>[
                RaisedButton(
                  onPressed: () => pickImage(),
                  child: Text('Select Image'),
                ),
                RaisedButton(
                  onPressed: () {
                    Navigator.of(context).push(
                      MaterialPageRoute<void>(
                          builder: (_) => ImagePage(
                                image: pickedImage,
                              )),
                    );
                  },
                  child: Text('Crop'),
                ),
              ]),
            ])),
      ),
    );
  }
}

class ImagePage extends StatefulWidget {
  ImagePage({Key key, @required this.image}) : super(key: key);

  final Image image;
  @override
  _ImagePageState createState() => _ImagePageState(image);
}

class _ImagePageState extends State<ImagePage> {
  _ImagePageState(this.image);
  final Image image;
  String _uploadedFileURL;
  final controller = CropController(aspectRatio: 1000 / 667.0);
  double _rotation = 0;

  @override
  void initState(){
    super.initState();
    controller.aspectRatio = 1;
    setState(() {

    });
  }

  void _cropImage() async {
    final pixelRatio = MediaQuery.of(context).devicePixelRatio;
    final cropped = await controller.crop(pixelRatio: pixelRatio);

    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => Scaffold(
          appBar: AppBar(
            title: Text('Crop Result'),
            centerTitle: true,
            actions: <Widget>[
              IconButton(
                icon: Icon(Icons.file_upload),
                tooltip: 'Upload',
                onPressed: () async {
                  // getting a directory path for saving
                  var dir = await getApplicationDocumentsDirectory();
                  final ByteData data = await cropped.toByteData(format: ImageByteFormat.png);
                  final buffer = data.buffer;

                  final String fileName = Random().nextInt(10000).toString() +'.png';
                  print(fileName);

                  final File newImage = await new File('${dir.path}/$fileName').writeAsBytes(
                      buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
                  await uploadFile(newImage);
                },
              ),
            ],
          ),
          body: Center(
            child: RawImage(
              image: cropped,
            ),
          ),
        ),
        fullscreenDialog: true,
      ),
    );
  }

  Future uploadFile(File img) async {
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Crop Demo'),
        centerTitle: true,
        actions: <Widget>[
          IconButton(
            onPressed: _cropImage,
            tooltip: 'Crop',
            icon: Icon(Icons.crop),
          )
        ],
      ),
      body: Column(
        children: <Widget>[
          Expanded(
            child: Container(
              color: Colors.black,
              padding: EdgeInsets.all(8),
              child: Crop(
                controller: controller,
                child: image,
                helper: Container(
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.white, width: 2),
                  ),
                ),
              ),
            ),
          ),
          Row(
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.undo),
                tooltip: 'Undo',
                onPressed: () {
                  controller.rotation = 0;
                  controller.scale = 1;
                  controller.offset = Offset.zero;
                  setState(() {
                    _rotation = 0;
                  });
                },
              ),
              Expanded(
                child: SliderTheme(
                  data: theme.sliderTheme.copyWith(
                    trackShape: CenteredRectangularSliderTrackShape(),
                  ),
                  child: Slider(
                    divisions: 361,
                    value: _rotation,
                    min: -180,
                    max: 180,
                    label: '$_rotationΒ°',
                    onChanged: (n) {
                      setState(() {
                        _rotation = n.roundToDouble();
                        controller.rotation = _rotation;
                      });
                    },
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

w/ "--web-renderer=html" Crop fails: `Unsupported operation: toImage is not supported on the Web`

I am using --dart-define=FLUTTER_WEB_USE_SKIA=true, on the Master channel and Crop works fine. But due to another requirement, I have to add the flag --web-renderer=html. With that additional flag, Crop fails.

Would you have any idea of a work around?

Here's my launch.json configuration

  {
      "name": "Chrome SKIA Dev",
      "request": "launch",
      "type": "dart",
      "program": "lib/main_web_dev.dart",
      "args": [
        "-d",
        "chrome",
        "--dart-define=FLUTTER_WEB_USE_SKIA=true",
        "--web-renderer=html"
      ]
    },

Here's the stack

Error: Unsupported operation: toImage is not supported on the Web
    at Object.throw_ [as throw] (http://localhost:65382/dart_sdk.js:4351:11)
    at _engine.SurfaceScene.new.toImage (http://localhost:65382/dart_sdk.js:158747:17)
    at layer$.OffsetLayer.new.toImage (http://localhost:65382/packages/flutter/src/rendering/layer.dart.lib.js:1636:30)
    at toImage.next (<anonymous>)
    at runBody (http://localhost:65382/dart_sdk.js:37988:34)
    at Object._async [as async] (http://localhost:65382/dart_sdk.js:38019:7)
    at layer$.OffsetLayer.new.toImage (http://localhost:65382/packages/flutter/src/rendering/layer.dart.lib.js:1627:20)
    at proxy_box.RenderRepaintBoundary.new.toImage (http://localhost:65382/packages/flutter/src/rendering/proxy_box.dart.lib.js:3355:26)
    at ui$._CropState.new.[_crop] (http://localhost:65382/packages/crop/src/ui.dart.lib.js:715:18)
    at ui$.CropController.new.crop (http://localhost:65382/packages/crop/src/ui.dart.lib.js:977:33)
    at crop_widget._CropWidgetPageState.new._cropImage (http://localhost:65382/packages/MyFamilyVoice/web/crop_widget.dart.lib.js:1712:46)
    at _cropImage.next (<anonymous>)
    at runBody (http://localhost:65382/dart_sdk.js:37988:34)
    at Object._async [as async] (http://localhost:65382/dart_sdk.js:38019:7)
    at crop_widget._CropWidgetPageState.new.[_cropImage] (http://localhost:65382/packages/MyFamilyVoice/web/crop_widget.dart.lib.js:1709:20)
    at ink_well._InkResponseState.new.[_handleTap] (http://localhost:65382/packages/flutter/src/material/icon_button.dart.lib.js:51085:42)
    at tap.TapGestureRecognizer.new.invokeCallback (http://localhost:65382/packages/flutter/src/gestures/recognizer.dart.lib.js:189:18)
    at tap.TapGestureRecognizer.new.handleTapUp (http://localhost:65382/packages/flutter/src/gestures/tap.dart.lib.js:395:40)
    at tap.TapGestureRecognizer.new.[_checkUp] (http://localhost:65382/packages/flutter/src/gestures/tap.dart.lib.js:201:12)
    at tap.TapGestureRecognizer.new.acceptGesture (http://localhost:65382/packages/flutter/src/gestures/tap.dart.lib.js:178:23)
    at arena.GestureArenaManager.new.sweep (http://localhost:65382/packages/flutter/src/gestures/arena.dart.lib.js:208:31)
    at binding$5.WidgetsFlutterBinding.new.handleEvent (http://localhost:65382/packages/flutter/src/gestures/binding.dart.lib.js:318:27)
    at binding$5.WidgetsFlutterBinding.new.dispatchEvent (http://localhost:65382/packages/flutter/src/gestures/binding.dart.lib.js:297:24)
    at binding$5.WidgetsFlutterBinding.new.dispatchEvent (http://localhost:65382/packages/flutter/src/rendering/layer.dart.lib.js:6093:13)
    at binding$5.WidgetsFlutterBinding.new.[_handlePointerEventImmediately] (http://localhost:65382/packages/flutter/src/gestures/binding.dart.lib.js:268:14)
    at binding$5.WidgetsFlutterBinding.new.handlePointerEvent (http://localhost:65382/packages/flutter/src/gestures/binding.dart.lib.js:241:43)
    at binding$5.WidgetsFlutterBinding.new.[_flushPointerEventQueue] (http://localhost:65382/packages/flutter/src/gestures/binding.dart.lib.js:230:14)
    at binding$5.WidgetsFlutterBinding.new.[_handlePointerDataPacket] (http://localhost:65382/packages/flutter/src/gestures/binding.dart.lib.js:220:65)
    at Object.invoke1 (http://localhost:65382/dart_sdk.js:178716:7)
    at _engine.EnginePlatformDispatcher.__.invokeOnPointerDataPacket (http://localhost:65382/dart_sdk.js:161367:15)
    at _engine.PointerBinding.__.[_onPointerData] (http://localhost:65382/dart_sdk.js:162002:49)
    at http://localhost:65382/dart_sdk.js:162435:26
    at http://localhost:65382/dart_sdk.js:162394:16
    at http://localhost:65382/dart_sdk.js:162102:11
Application finished.

Here's doctor:

flutter doctor -v
[βœ“] Flutter (Channel master, 1.25.0-5.0.pre.57, on macOS 11.0.1 20B29 darwin-x64, locale en-US)
    β€’ Flutter version 1.25.0-5.0.pre.57 at /Users/bartonhammond/tools/flutter
    β€’ Framework revision b358854172 (18 hours ago), 2020-12-03 22:41:37 +0100
    β€’ Engine revision 20caf54969
    β€’ Dart version 2.12.0 (build 2.12.0-76.0.dev)

[βœ“] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
    β€’ Android SDK at /Users/bartonhammond/Library/Android/sdk
    β€’ Platform android-29, build-tools 29.0.2
    β€’ Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS (Xcode 12.1)
    β€’ Xcode at /Applications/Xcode.app/Contents/Developer
    β€’ Xcode 12.1, Build version 12A7403
    β€’ CocoaPods version 1.9.3

[βœ“] Chrome - develop for the web
    β€’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[βœ“] Android Studio (version 4.1)
    β€’ Android Studio at /Applications/Android Studio.app/Contents
    β€’ Flutter plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/9212-flutter
    β€’ Dart plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/6351-dart
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)

[βœ“] VS Code (version 1.51.1)
    β€’ VS Code at /Applications/Visual Studio Code.app/Contents
    β€’ Flutter extension version 3.16.0

[βœ“] Connected device (2 available)
    β€’ iPhone SE (2nd generation) (mobile) β€’ 5D61B4C3-F813-4E6D-BD30-D9A81F0C867C β€’ ios            β€’ com.apple.CoreSimulator.SimRuntime.iOS-14-1 (simulator)
    β€’ Chrome (web)                        β€’ chrome                               β€’ web-javascript β€’ Google Chrome 87.0.4280.88

β€’ No issues found!

Unsupported operation: toImage is not supported on the Web

When using flutter_crop in flutter web i get this exception:

errors.dart:165 Uncaught (in promise) Error: Unsupported operation: toImage is not supported on the Web
at Object.throw_ [as throw] (errors.dart:214)
at _engine.SurfaceScene.new.toImage (scene.dart:20)
at layer$.OffsetLayer.new.toImage (layer.dart:1246)
at toImage.next (<anonymous>)
at runBody (async_patch.dart:84)
at Object._async [as async] (async_patch.dart:123)
at layer$.OffsetLayer.new.toImage (layer.dart:1230)
at proxy_box.RenderRepaintBoundary.new.toImage (proxy_box.dart:2957)
at ui$.CropController.new.crop (ui.dart:374)
at image_crop_view_model.ImageCropViewModel.new.cropImage (image_crop_view_model.dart:41)
at cropImage.next (<anonymous>)
at runBody (async_patch.dart:84)
at Object._async [as async] (async_patch.dart:123)
at image_crop_view_model.ImageCropViewModel.new.cropImage (image_crop_view_model.dart:37)
at ink_well._InkResponseStateWidget.new.<anonymous> (image_crop_view_desktop.dart:25)
at ink_well._InkResponseState.new.[_handleTap] (ink_well.dart:953)
at ink_well.dart:1059
at tap.TapGestureRecognizer.new.invokeCallback (recognizer.dart:182)
at tap.TapGestureRecognizer.new.handleTapUp (tap.dart:522)
at tap.TapGestureRecognizer.new.[_checkUp] (tap.dart:282)
at tap.TapGestureRecognizer.new.acceptGesture (tap.dart:254)
at arena.GestureArenaManager.new.sweep (arena.dart:156)
at binding$5.WidgetsFlutterBinding.new.handleEvent (binding.dart:222)
at binding$5.WidgetsFlutterBinding.new.dispatchEvent (binding.dart:198)
at binding$5.WidgetsFlutterBinding.new.[_handlePointerEvent] (binding.dart:156)
at binding$5.WidgetsFlutterBinding.new.[_flushPointerEventQueue] (binding.dart:102)
at binding$5.WidgetsFlutterBinding.new.[_handlePointerDataPacket] (binding.dart:86)
at Object._invoke1 (window.dart:740)
at _engine.EngineWindow.new.invokeOnPointerDataPacket (window.dart:354)
at _engine.PointerBinding.__.[_onPointerData] (pointer_binding.dart:129)
at pointer_binding.dart:479
at pointer_binding.dart:440
at pointer_binding.dart:210
DartError @ errors.dart:165
throw_ @ errors.dart:214
toImage @ scene.dart:20
toImage @ layer.dart:1246
runBody @ async_patch.dart:84
_async @ async_patch.dart:123
toImage @ layer.dart:1230
toImage @ proxy_box.dart:2957
crop @ ui.dart:374
cropImage @ image_crop_view_model.dart:41
runBody @ async_patch.dart:84
_async @ async_patch.dart:123
cropImage @ image_crop_view_model.dart:37
(anonymous) @ image_crop_view_desktop.dart:16    
[_handleTap] @ ink_well.dart:953
(anonymous) @ ink_well.dart:1044
invokeCallback @ recognizer.dart:182
handleTapUp @ tap.dart:521
[_checkUp] @ tap.dart:282
acceptGesture @ tap.dart:254
sweep @ arena.dart:156
handleEvent @ binding.dart:222
dispatchEvent @ binding.dart:198
[_handlePointerEvent] @ binding.dart:156
[_flushPointerEventQueue] @ binding.dart:102
[_handlePointerDataPacket] @ binding.dart:85
_invoke1 @ window.dart:740
invokeOnPointerDataPacket @ window.dart:355
[_onPointerData] @ pointer_binding.dart:129
(anonymous) @ pointer_binding.dart:479
(anonymous) @ pointer_binding.dart:440
(anonymous) @ pointer_binding.dart:210
Promise.then (async)
_scheduleImmediateWithPromise @ async_patch.dart:167
_scheduleImmediate @ async_patch.dart:136
_scheduleAsyncCallback @ schedule_microtask.dart:72
_rootScheduleMicrotask @ zone.dart:1257
scheduleMicrotask @ schedule_microtask.dart:140
[_tryToResolveArena] @ arena.dart:231
[_resolve] @ arena.dart:214
resolve @ arena.dart:52
resolve @ recognizer.dart:260
resolve @ long_press.dart:457
handlePrimaryPointer @ long_press.dart:333
handleEvent @ recognizer.dart:475
[_dispatch] @ pointer_router.dart:76
(anonymous) @ pointer_router.dart:122
forEach @ linked_hash_map.dart:21
[_dispatchEventToRoutes] @ pointer_router.dart:124
route @ pointer_router.dart:110
handleEvent @ binding.dart:218
dispatchEvent @ binding.dart:198
[_handlePointerEvent] @ binding.dart:156
[_flushPointerEventQueue] @ binding.dart:102
[_handlePointerDataPacket] @ binding.dart:85
_invoke1 @ window.dart:740
invokeOnPointerDataPacket @ window.dart:355
[_onPointerData] @ pointer_binding.dart:129
(anonymous) @ pointer_binding.dart:479
(anonymous) @ pointer_binding.dart:440
(anonymous) @ pointer_binding.dart:210

Disable rotation?

Is there a way to disable rotation to only accept scale input? I wanna zoom in without touching the rotation of the image

Cropped image of type 'CkImage' instead of 'Image'

I am using this package for flutter web. I try to return the cropped image of cropped = controller.crop(...) to the previous screen by Navigator.pop(context,cropped).
Somehow the previous screen receives an image of type 'CkImage' and not of type Image, as I would have expected it.

PersistedOffset: is in an unexpected state error

Hi again,

I am always getting the error below when I try to crop an image :

The following PersistedSurfaceException was thrown during a scheduler callback:

PersistedOffset: is in an unexpected state.

Expected one of: PersistedSurfaceState.active, PersistedSurfaceState.released

But was: PersistedSurfaceState.created

I have made some research on the internet but couldn't find a way to solve this error. I can share my code if needed.

Thanks.

Unsupported operation: LayerScene.toImage not implemented

I implemented the crop widget to an existing application and now it is throwing an error below, at this line:

return rrb.toImage(pixelRatio: pixelRatio);

Maybe I am importing a wrong library somewhere. Do you have any idea?
The error occurs when I click the crop button.

Uncaught (in promise) Error: Unsupported operation: LayerScene.toImage not implemented.
    at Object.throw_ [as throw] (errors.dart:216)
    at _engine.LayerScene.new.toImage (layer_scene_builder.dart:20)
    at layer$.OffsetLayer.new.toImage (layer.dart:1206)
    at toImage.next (<anonymous>)
    at runBody (async_patch.dart:84)
    at Object._async [as async] (async_patch.dart:123)
    at layer$.OffsetLayer.new.toImage (layer.dart:1190)
    at proxy_box.RenderRepaintBoundary.new.toImage (proxy_box.dart:2968)
    at ui$._CropState.new.[_crop] (ui.dart:80)
    at ui$.CropController.new.crop (ui.dart:409)
    at crop_widget._CropWidgetState.new.<anonymous> (crop_widget.dart:47)
    at Generator.next (<anonymous>)
    at runBody (async_patch.dart:84)
    at Object._async [as async] (async_patch.dart:123)
    at crop_widget.dart:44
    at ink_well._InkResponseState.new.[_handleTap] (ink_well.dart:985)
    at ink_well.dart:1101
    at tap.TapGestureRecognizer.new.invokeCallback (recognizer.dart:183)
    at tap.TapGestureRecognizer.new.handleTapUp (tap.dart:598)
    at tap.TapGestureRecognizer.new.[_checkUp] (tap.dart:287)
    at tap.TapGestureRecognizer.new.acceptGesture (tap.dart:259)
    at arena.GestureArenaManager.new.sweep (arena.dart:157)
    at binding$5.WidgetsFlutterBinding.new.handleEvent (binding.dart:372)
    at binding$5.WidgetsFlutterBinding.new.dispatchEvent (binding.dart:348)
    at binding$5.WidgetsFlutterBinding.new.dispatchEvent (binding.dart:268)
    at binding$5.WidgetsFlutterBinding.new.[_handlePointerEventImmediately] (binding.dart:303)
    at binding$5.WidgetsFlutterBinding.new.handlePointerEvent (binding.dart:267)
    at binding$5.WidgetsFlutterBinding.new.[_flushPointerEventQueue] (binding.dart:225)
    at binding$5.WidgetsFlutterBinding.new.[_handlePointerDataPacket] (binding.dart:208)
    at Object._invoke1 (window.dart:853)
    at _engine.EngineWindow.new.invokeOnPointerDataPacket (window.dart:389)
    at _engine.PointerBinding.__.[_onPointerData] (pointer_binding.dart:129)
    at pointer_binding.dart:479
    at pointer_binding.dart:440
    at pointer_binding.dart:210

Silence Print Statements

line 290 in ui.dart prints the rotation value, would be good to remove this, or put it under a log flag.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.