[Bug]: Cropping clamp errors due to certain aspect ratios
Package Version
9.9.0
Flutter Version
3.29
Platforms
Linux, macOS, Windows, Web, iOS, Android
How to reproduce?
Using the crop_to_main_editor.dart example, create a CropRotateEditorConfigs with an initAspectRatio of 20/3.
final ProImageEditorConfigs _editorConfigs = ProImageEditorConfigs(
designMode: platformDesignMode,
cropRotateEditor: const CropRotateEditorConfigs(
initAspectRatio: 20/3,
enableProvideImageInfos: true,
showAspectRatioButton: false,
),
);
Once you display this example, attempt to resize the crop area vertically.
Notice that the lowerLimit of the clamp is greater than its upperLimit.
The only way I was able to not get it to crash was to modify the switch statement in CropRotateEditorState._onScaleUpdate so that the clamp's lower bound was the smallest of either the original lower bound for that section or its upper bound:
switch (_currentCropAreaPart) {
case CropAreaPart.topLeft:
cropRect = Rect.fromLTRB(
dx.clamp(minLeft, maxRight),
// fix
dy.clamp(min(minTop, maxBottom), maxBottom),
cropRect.right,
cropRect.bottom,
);
break;
case CropAreaPart.topRight:
cropRect = Rect.fromLTRB(
cropRect.left,
// fix
dy.clamp(min(minTop, maxBottom), maxBottom),
dx.clamp(cornerGap + cropRect.left, minRight),
cropRect.bottom,
);
break;
case CropAreaPart.bottomLeft:
cropRect = Rect.fromLTRB(
dx.clamp(minLeft, maxRight),
cropRect.top,
cropRect.right,
dy.clamp(min(cornerGap + cropRect.top, maxBottom), minBottom),
);
break;
case CropAreaPart.bottomRight:
cropRect = Rect.fromLTRB(
cropRect.left,
cropRect.top,
dx.clamp(cornerGap + cropRect.left, minRight),
// fix
dy.clamp(min(cornerGap + cropRect.top, minBottom), minBottom),
);
break;
case CropAreaPart.left:
cropRect = Rect.fromLTRB(
dx.clamp(minLeft, maxRight),
cropRect.top,
cropRect.right,
cropRect.bottom,
);
_setOffsetLimits();
break;
case CropAreaPart.right:
cropRect = Rect.fromLTRB(
cropRect.left,
cropRect.top,
dx.clamp(cornerGap + cropRect.left, minRight),
cropRect.bottom,
);
break;
case CropAreaPart.top:
cropRect = Rect.fromLTRB(
cropRect.left,
// fix
dy.clamp(minTop, max(cornerGap + cropRect.bottom, minBottom)),
cropRect.right,
cropRect.bottom,
);
break;
case CropAreaPart.bottom:
cropRect = Rect.fromLTRB(
cropRect.left,
cropRect.top,
cropRect.right,
// fix
dy.clamp(min(cornerGap + cropRect.top, minBottom), minBottom),
);
break;
default:
break;
}
However, I dunno if this is the correct way to determine the clamp's true lower bounds.
Note that if you use a portrait based aspect ratio (e.g. 5/7), it doesn't run into this issue. Perhaps it's not accounting the orientation of the crop area.
Logs (optional)
======== Exception caught by gesture ===============================================================
The following ArgumentError was thrown while handling a gesture:
Invalid argument(s): 298.9714285714286
When the exception was thrown, this was the stack:
#0 double.clamp (dart:core-patch/double.dart:232:7)
#1 CropRotateEditorState._onScaleUpdate (package:pro_image_editor/features/crop_rotate_editor/crop_rotate_editor.dart:1594:20)
#2 ScaleGestureRecognizer._advanceStateMachine.<anonymous closure> (package:flutter/src/gestures/scale.dart:746:20)
#3 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:357:24)
#4 ScaleGestureRecognizer._advanceStateMachine (package:flutter/src/gestures/scale.dart:745:9)
#5 ScaleGestureRecognizer.handleEvent (package:flutter/src/gestures/scale.dart:573:7)
#6 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:97:12)
#7 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:143:9)
#8 _LinkedHashMapMixin.forEach (dart:_compact_hash:763:13)
#9 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18)
#10 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:131:7)
#11 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:530:19)
#12 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:499:22)
#13 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:460:11)
#14 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:437:7)
#15 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:394:5)
#16 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:341:7)
#17 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:308:9)
#18 _invoke1 (dart:ui/hooks.dart:332:13)
#19 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:451:7)
#20 _dispatchPointerDataPacket (dart:ui/hooks.dart:267:31)
Handler: "onUpdate"
Recognizer: ScaleGestureRecognizer#c9ffc
debugOwner: CropRotateGestureDetectorState#a5af0
====================================================================================================