chooyan-eng / crop_your_image Goto Github PK
View Code? Open in Web Editor NEWA flutter plugin which provides Crop Widget for cropping images.
Home Page: https://pub.dev/packages/crop_your_image
License: Apache License 2.0
A flutter plugin which provides Crop Widget for cropping images.
Home Page: https://pub.dev/packages/crop_your_image
License: Apache License 2.0
The goal of API should be
Crop(
image: _imageData,
controller: _controller,
initialArea: _rectBasedOnImageData, // <- ADD new property here.
onCrop: (croppedData) {},
),
As initial area based on actual screen is up to users device, initialArea
must be based on actual image size.
initialArea
is converted into the initial rect based on the size of users' screen and set as initial cropping area.
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.
Is there any way to get a file from oncropped property? How can I convert uint8list to a File.
Situation: After cropping, onCropped and onStatusChanged with status= ready were called. At this time the crop widget still shows the progress indicator for a while loading the cropped image
Following Exception when I start a new crop before the progress indicator has gone:
E/flutter (17760): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: 'package:crop_your_image/src/crop.dart': Failed assertion: line 412 pos 12: '_targetImage != null': is not true.
E/flutter (17760): #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
E/flutter (17760): #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
E/flutter (17760): #2 _CropEditorState._crop (package:crop_your_image/src/crop.dart:412:12)
E/flutter (17760): #3 CropController.crop (package:crop_your_image/src/controller.dart:11:34)
E/flutter (17760): #4 _BildausschnittRouteState._cropImage (package:xyz/route/BildausschnittRoute.dart:242:16)
E/flutter (17760): #5 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1072:21)
E/flutter (17760): #6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:253:24)
E/flutter (17760): #7 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:627:11)
E/flutter (17760): #8 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:306:5)
E/flutter (17760): #9 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:239:7)
E/flutter (17760): #10 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:615:9)
E/flutter (17760): #11 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:98:12)
E/flutter (17760): #12 PointerRouter._dispatchEventToRoutes. (package:flutter/src/gestures/pointer_router.dart:143:9)
E/flutter (17760): #13 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:617:13)
E/flutter (17760): #14 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18)
E/flutter (17760): #15 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:127:7)
E/flutter (17760): #16 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:460:19)
E/flutter (17760): #17 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:440:22)
E/flutter (17760): #18 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:337:11)
E/flutter (17760): #19 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:395:7)
E/flutter (17760): #20 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:357:5)
E/flutter (17760): #21 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:314:7)
E/flutter (17760): #22 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:295:7)
E/flutter (17760): #23 _invoke1 (dart:ui/hooks.dart:167:13)
E/flutter (17760): #24 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:341:7)
E/flutter (17760): #25 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31)
E/flutter (17760):
Is there a way to be informed when _target image is set, so I can block possibility for start new crop till this point?
Since crop function is not async I am not sure how to show a loading indicator while the image is being cropped, any suggestions ?
Image zoom makes more reliable to use this plugin.
Thank you for making such a good thing in flutter. it's really helpful.
I can not able to rotate and flip(vertically + horizontally) image in Your Crop method.
My code:
Crop(
controller: _controller,
image: widget.imageFile.readAsBytesSync(),
onCropped: (cropped) {
croppedData = cropped;
isProcessing = false;
},
initialSize: 0.7,
baseColor: Colors.grey.shade700,
cornerDotBuilder: (size, cornerIndex) {
return _isPreviewing ? const SizedBox.shrink() : const DotControl();
},
maskColor: _isPreviewing ? Colors.white : null)
Third line in code: image: widget.imageFile.readAsBytesSync(), NEED THIS IMAGE TO ROTATE AND FLIP
Please help me with this.
Thank you.
dear @chooyan-eng
circle frame on flutter web mobile chrome has problem
i tried it with this command :
flutter run -d web-server --web-port 8080 --web-hostname 0.0.0.0
you can see result in this video :
circle shape frame doesn't work
I have issue too long when cropping image at Web Platform - At android running well.
Need improvement at cropping process at web platform
Thanks
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.
Use wechat_assets_picker: ^7.0.5 to pick pictures, and the photo[0].originBytes method is converted to Uint8List.
ios, Android, some pictures can be selected, and some pictures will always be loaded.
`
util.requestPhotosPermission().then((value) {
if(!value) return;
});
final List? photo = await AssetPicker.pickAssets(context,
pickerConfig: const AssetPickerConfig(
maxAssets: 1,
gridCount: 3,
pageSize: 15,
requestType: RequestType.image,
gridThumbnailSize: ThumbnailSize.square(100)
));
if(photo != null){
Uint8List? filePath = await photo[0].originBytes;
if(filePath == null){
MyView.showToast('Image selection failed');
return;
}
Navigator.push(context, MaterialPageRoute(builder: (context)=>CropImage(
fileimage: filePath,
result: (value) {
processImage(value);
},
)));
}
`
@override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, body: Stack( children: [ Column( children: [ Container(height: ScreenService.topSafeHeight), Expanded( child: Crop( image: widget.fileimage, controller: controller, onCropped: (image) { Navigator.pop(context); widget.result(image); }, aspectRatio: 1, initialSize: 0.8, withCircleUi: true, baseColor: Colors.black, maskColor: Colors.black.withAlpha(150), cornerDotBuilder: (size, edgeAlignment) => const DotControl(color: Colors.white54), )), GestureDetector( onTap: (){ controller.crop(); }, child: Container( height: 50, width: ScreenService.width, color: MyView.mainColor, child: const Center( child: Text( 'Crop Avatar', style: TextStyle( color: Colors.white, fontFamily: 'bold', fontSize: 18 ), ), ) ), ) ], ), Positioned( top: 40,right: 20, child: GestureDetector( onTap: (){ Navigator.pop(context); }, child: const Icon(Icons.clear,size: 35,color: Colors.white,), ), ) ], ), ); }
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!
This plugin does not support UNDO operation
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 :
Colors.blue.shade50
is set to Container.color
now.
The goal is to accept configuration by app and apply here with default Colors.white
.
I don't know what this function that to do but it need too much time to done.
@override
void didChangeDependencies() {
final future = compute(_fromByteData, widget.image);
_lastComputed = future;
future.then((converted) {
if (_lastComputed == future) {
_targetImage = converted;
_withCircleUi = widget.withCircleUi;
_resetCroppingArea();
setState(() {
_lastComputed = null;
});
widget.onStatusChanged?.call(CropStatus.ready);
}
});
super.didChangeDependencies();
}
And I suggest for you that CircleProgressLoading
should be customizable.
flutter pub get is returning server not available just for this particular package
The code in this line takes a very long time to execute
crop_your_image/lib/src/crop.dart
Line 192 in 7bf3842
I think it must be async or move to Isolate.
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.
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")),
))
],
)
],
),
);
},
),
I have tested these on several images and when cropping it multiplies its size up to x3. Why is that? Am I doing something wrong?
flutter: Bytes before cropping 1930990 (1.9 MB)
flutter: Bytes after cropping 4916747 (4.9 MB)
class PictureCroppingPage extends StatefulWidget {
const PictureCroppingPage({
Key? key,
required this.picture,
required this.onCropped,
this.isCircular = false,
this.aspectRatio,
}) : super(key: key);
final Uint8List picture;
final Function(Uint8List) onCropped;
final bool isCircular;
final double? aspectRatio;
@override
_PictureCroppingPageState createState() => _PictureCroppingPageState();
}
class _PictureCroppingPageState extends State<PictureCroppingPage> {
final _controller = CropController();
bool isCropping = false;
@override
Widget build(BuildContext context) {
print('Bytes before cropping ${widget.picture.lengthInBytes}');
return Scaffold(
appBar: AppBar(
leading: const CloseButton(),
title: Text(context.l10n.cropPicturePageTitle),
elevation: 1.0,
),
body: Crop(
controller: _controller,
image: widget.picture,
onCropped: (picture) {
widget.onCropped(picture);
print('Bytes after cropping ${picture.lengthInBytes}');
context.navigator.pop();
},
aspectRatio: widget.aspectRatio,
withCircleUi: widget.isCircular,
baseColor: context.theme.backgroundColor,
),
bottomNavigationBar: BottomBarAction(
builder: (context) {
return DivenElevatedButton(
isLoading: isCropping,
isDisabled: isCropping,
onPressed: () {
_controller.crop();
setState(() {
isCropping = true;
});
},
label: Text(
context.l10n.cropPicturePageCroppingButton,
),
);
},
),
);
}
}
When closing page with CropEditor there is error in the console.
It happens in the method _resetCroppingArea when getting screenSize from context:
void _resetCroppingArea() {
final screenSize = MediaQuery.of(context).size;
final imageRatio = _targetImage!.width / _targetImage!.height;
_isFitVertically = imageRatio < screenSize.aspectRatio;
_imageRect = calculator.imageRect(screenSize, imageRatio);
_resizeWith(widget.aspectRatio, widget.initialArea);
}
The general way of cropping with this package is to change the rectangular box's dimensions and position. It's very good indeed.
I would like you to provide an opposite way of that. Let us change the position and scale of the underlying image by panning & zooming while keeping the cropper box at the center with a selected aspect ratio.
So instead of us moving the cropping rectangle, we would be dragging the image itself until it is positioned in a stationary cropping frame.
It will be like the cropper of the Instagram's post sharing screen. This is very intuitive for users to adapt.
There is another package on pub.dev for cropping as I said; however, I think your package is better at the moment and if you can implement what I say in this issue, it will be superior.
Thanks a lot!
Currently the initialArea
expects values based on the image size and not on the screen size.
This is cumbersome, because using the lib the developer does provide byte data and not image data. Hence, the actual image data is not available outside the lib. The developer then needs to build her own version of _fromByteData
(see https://github.com/chooyan-eng/crop_your_image/blob/main/lib/src/crop.dart#L537 ) to get the _targetImage
.
It would be much more convenient to provide a way that is based on the screen size. The lib then should transform this to actual image sizes by taking into account the ratio between screen size and the actual image data.
This could be an implementation:
// given a screen based area
final initialScreenBasedArea = Rect(...)
// calculate the initalArea based on the image
final img = _fromByteData(byteData);
final screen = MediaQuery.of(context).size;
final imageScreenRatioHorizontal = img.width / screen.width;
final imageScreenRatioVertical = img.height / screen.height;
initialArea = Rect.fromCenter(
width: initialScreenBasedArea.width * imageScreenRatioHorizontal,
height: initialScreenBasedArea.height * imageScreenRatioVertical,
center: Offset(
initialScreenBasedArea.center.dx * imageScreenRatioHorizontal,
initialScreenBasedArea.center.dy * imageScreenRatioVertical,
),
);
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.
I am getting error in running flutter pub get
because version solving fails.
Because every version of flutter_driver from sdk depends on webdriver 2.1.2 which depends on archive >=1.0.0 <3.0.0, every version of flutter_driver from sdk requires archive >=1.0.0 <3.0.0. And because every version of crop_your_image depends on image ^3.0.1 which depends on archive ^3.0.0, flutter_driver from sdk is incompatible with crop_your_image. So, because project depends on both crop_your_image ^0.5.3 and flutter_driver any from sdk, version solving failed. Running "flutter pub get" in project... pub get failed (1; So, because project depends on both crop_your_image ^0.5.3 and flutter_driver any from sdk, version solving failed.)
As the version is clashing with flutter sdk I cannot understand how to fix. Let me know if I need to add any more required information to help me fix it.
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
crop_your_image/lib/src/crop.dart
Line 369 in c807944
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:
Currently the rect
in onMoved
is in logical or UI pixels. It would be great if we support actual image pixels as well.
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.
I use Crop
in interactive: true, fixArea: true
mode. It work great, but encountered two situations where API wasn't adequate with interactive mode:
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.