From 90dc3369fafd88d5c3db13cff381fdcee2fdb115 Mon Sep 17 00:00:00 2001 From: hm21 Date: Sat, 27 Dec 2025 08:48:22 +0100 Subject: [PATCH 1/2] feat(crop-rotate-editor): add onTransformUpdateEnd callback and complete parameters handling --- .../crop_rotate_editor_callbacks.dart | 16 ++++ .../crop_rotate_editor.dart | 43 +-------- .../mixins/crop_area_history.dart | 88 +++++++++++++++++++ 3 files changed, 107 insertions(+), 40 deletions(-) diff --git a/lib/core/models/editor_callbacks/crop_rotate_editor_callbacks.dart b/lib/core/models/editor_callbacks/crop_rotate_editor_callbacks.dart index 11fd90d99..6205e21bc 100644 --- a/lib/core/models/editor_callbacks/crop_rotate_editor_callbacks.dart +++ b/lib/core/models/editor_callbacks/crop_rotate_editor_callbacks.dart @@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart'; // Project imports: +import '../complete_parameters.dart'; import 'editor_callbacks_typedef.dart'; import 'standalone_editor_callbacks.dart'; @@ -18,6 +19,7 @@ class CropRotateEditorCallbacks extends StandaloneEditorCallbacks { this.onDoubleTap, this.onResize, this.onReset, + this.onTransformUpdateEnd, super.onInit, super.onAfterViewInit, super.onUndo, @@ -64,6 +66,18 @@ class CropRotateEditorCallbacks extends StandaloneEditorCallbacks { /// A callback function that is triggered when a reset action is performed. final Function()? onReset; + /// Callback that is triggered when a transformation update ends. + /// + /// This callback is invoked when the user completes a gesture that modifies + /// the crop or rotation transformation (e.g., releasing a pinch gesture or + /// finishing a rotation gesture). + /// + /// The [parameters] contain information about the completed transformation, + /// including the final state of the crop and rotation values. + /// + /// **IMPORTANT:** The `imageBytes` will always be empty. + final Function(CompleteParameters parameters)? onTransformUpdateEnd; + /// Handles the rotate start event. /// /// This method calls the [onRotateStart] callback with the provided [value] @@ -159,6 +173,7 @@ class CropRotateEditorCallbacks extends StandaloneEditorCallbacks { Function()? onRedo, Function()? onUndo, Function()? onCloseEditor, + Function(CompleteParameters parameters)? onTransformUpdateEnd, }) { return CropRotateEditorCallbacks( onRotateStart: onRotateStart ?? this.onRotateStart, @@ -177,6 +192,7 @@ class CropRotateEditorCallbacks extends StandaloneEditorCallbacks { onRedo: onRedo ?? this.onRedo, onUndo: onUndo ?? this.onUndo, onCloseEditor: onCloseEditor ?? this.onCloseEditor, + onTransformUpdateEnd: onTransformUpdateEnd ?? this.onTransformUpdateEnd, ); } } diff --git a/lib/features/crop_rotate_editor/crop_rotate_editor.dart b/lib/features/crop_rotate_editor/crop_rotate_editor.dart index 20c4911de..35a3fd900 100644 --- a/lib/features/crop_rotate_editor/crop_rotate_editor.dart +++ b/lib/features/crop_rotate_editor/crop_rotate_editor.dart @@ -833,46 +833,9 @@ class CropRotateEditorState extends State /// Return complete parameters if requested if (initConfigs.callbacks.onCompleteWithParameters != null) { - final isTransformed = transformC.isNotEmpty; - - Size originalImageSize; - if (isVideoEditor) { - originalImageSize = videoController!.initialResolution; - } else { - var rawOriginalSize = - await widget.editorImage?.safeByteArray(context) ?? imageBytes; - var decodedImage = await decodeImageFromList(rawOriginalSize); - originalImageSize = Size( - decodedImage.width.toDouble(), - decodedImage.height.toDouble(), - ); - } - - Size? outputSize = transformC.getCropSize(originalImageSize); - Offset? outputOffset = transformC.getCropStartOffset(originalImageSize); - - await callbacks.onCompleteWithParameters?.call( - CompleteParameters( - blur: appliedBlurFactor, - matrixFilterList: appliedFilters, - matrixTuneAdjustmentsList: - appliedTuneAdjustments.map((item) => item.matrix).toList(), - cropWidth: isTransformed ? outputSize.width.round() : null, - cropHeight: isTransformed ? outputSize.height.round() : null, - cropX: isTransformed ? outputOffset.dx.round() : null, - cropY: isTransformed ? outputOffset.dy.round() : null, - flipX: - transformC.is90DegRotated ? transformC.flipY : transformC.flipX, - flipY: - transformC.is90DegRotated ? transformC.flipX : transformC.flipY, - rotateTurns: transformC.angleToTurns(), - startTime: null, - endTime: null, - image: imageBytes, - isTransformed: isTransformed, - layers: layers ?? [], - ), - ); + final completeParams = + await getCompleteParameters(imageBytes: imageBytes); + await callbacks.onCompleteWithParameters?.call(completeParams); } LoadingDialog.instance.hide(); diff --git a/lib/features/crop_rotate_editor/mixins/crop_area_history.dart b/lib/features/crop_rotate_editor/mixins/crop_area_history.dart index 16474b204..a0f2bc19d 100644 --- a/lib/features/crop_rotate_editor/mixins/crop_area_history.dart +++ b/lib/features/crop_rotate_editor/mixins/crop_area_history.dart @@ -1,11 +1,13 @@ // Dart imports: import 'dart:math'; +import 'dart:typed_data'; // Flutter imports: import 'package:flutter/material.dart'; // Project imports: import '/core/mixins/standalone_editor.dart'; +import '/core/models/complete_parameters.dart'; import '/core/models/init_configs/crop_rotate_editor_init_configs.dart'; import '/shared/widgets/extended/extended_custom_paint.dart'; import '/shared/widgets/extended/extended_transform_scale.dart'; @@ -298,10 +300,21 @@ mixin CropAreaHistory ), ); screenshotHistoryPosition++; + _handleTransformationUpdateEnd(); setState(() {}); takeScreenshot(); } + void _handleTransformationUpdateEnd() async { + final callback = cropRotateEditorCallbacks?.onTransformUpdateEnd; + if (callback == null) return; + + final completeParams = + await getCompleteParameters(imageBytes: Uint8List(0)); + + callback(completeParams); + } + /// Clears forward changes from the history. void cleanForwardChanges() { if (history.length > 1) { @@ -325,6 +338,7 @@ mixin CropAreaHistory _setParametersFromHistory(); } cropRotateEditorCallbacks?.handleUndo(); + _handleTransformationUpdateEnd(); setState(() {}); } } @@ -335,6 +349,7 @@ mixin CropAreaHistory screenshotHistoryPosition++; _setParametersFromHistory(); cropRotateEditorCallbacks?.handleRedo(); + _handleTransformationUpdateEnd(); setState(() {}); } } @@ -446,4 +461,77 @@ mixin CropAreaHistory /// overridden to implement specific fitting logic. @protected void calcFitToScreen() {} + + /// Generates complete parameters for the image transformation process. + /// + /// This method calculates all the transformation parameters needed to export + /// the edited image, including crop dimensions, rotation, flip operations, + /// filters, and blur effects. + /// + /// The method handles both image and video editing modes: + /// - For video editing: uses the video controller's initial resolution + /// - For image editing: decodes the original image to get its dimensions + /// + /// Parameters: + /// * [imageBytes] - The original image data as bytes + /// + /// Returns a [CompleteParameters] object containing: + /// * Crop dimensions (width, height) and offset (x, y) if transformed + /// * Flip operations in x and y directions (adjusted for 90° rotations) + /// * Rotation in turns + /// * Applied blur factor + /// * List of matrix filters + /// * List of tune adjustment matrices + /// * Layer data + /// * Original image bytes + /// * Transformation status flag + /// + /// The crop dimensions and offsets are only included when [isTransformed] + /// is true. Flip operations are automatically adjusted when the image + /// is rotated by 90 degrees to maintain correct orientation. + Future getCompleteParameters({ + required Uint8List imageBytes, + }) async { + TransformConfigs transformC = + !canRedo && !canUndo && initialTransformConfigs != null + ? initialTransformConfigs! + : activeHistory; + + final isTransformed = transformC.isNotEmpty; + + Size originalImageSize; + if (isVideoEditor) { + originalImageSize = videoController!.initialResolution; + } else { + var rawOriginalSize = + await widget.editorImage?.safeByteArray(context) ?? imageBytes; + var decodedImage = await decodeImageFromList(rawOriginalSize); + originalImageSize = Size( + decodedImage.width.toDouble(), + decodedImage.height.toDouble(), + ); + } + + Size? outputSize = transformC.getCropSize(originalImageSize); + Offset? outputOffset = transformC.getCropStartOffset(originalImageSize); + + return CompleteParameters( + blur: appliedBlurFactor, + matrixFilterList: appliedFilters, + matrixTuneAdjustmentsList: + appliedTuneAdjustments.map((item) => item.matrix).toList(), + cropWidth: isTransformed ? outputSize.width.round() : null, + cropHeight: isTransformed ? outputSize.height.round() : null, + cropX: isTransformed ? outputOffset.dx.round() : null, + cropY: isTransformed ? outputOffset.dy.round() : null, + flipX: transformC.is90DegRotated ? transformC.flipY : transformC.flipX, + flipY: transformC.is90DegRotated ? transformC.flipX : transformC.flipY, + rotateTurns: transformC.angleToTurns(), + startTime: null, + endTime: null, + image: imageBytes, + isTransformed: isTransformed, + layers: layers ?? [], + ); + } } From f27109138fccbd160dbac08fe6803129a67b3bc0 Mon Sep 17 00:00:00 2001 From: hm21 Date: Sat, 27 Dec 2025 08:51:51 +0100 Subject: [PATCH 2/2] chore(release): bump version to 11.15.2 and update changelog --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ef6da60b..85328cfa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 11.15.2 +- **FEAT**(crop-rotate-editor): Add new callback `onTransformUpdateEnd` that returns all transformation changes whenever a value in the crop-rotate editor is modified. + ## 11.15.1 - **FEAT**(text-editor): Add config `enableAutoWrapOnLayer` to the `TextEditorConfigs` which allows for deciding whether the layer applies the editor's auto wrapping or not. More details in PR [#720](https://github.com/hm21/pro_image_editor/pull/720). diff --git a/pubspec.yaml b/pubspec.yaml index 7e48fca62..b01d40269 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: pro_image_editor description: "A Flutter image editor: Seamlessly enhance your images with user-friendly editing features." -version: 11.15.1 +version: 11.15.2 homepage: https://github.com/hm21/pro_image_editor/ repository: https://github.com/hm21/pro_image_editor/ documentation: https://github.com/hm21/pro_image_editor/