diff --git a/docs/assets/resize_scale.gif b/docs/assets/resize_scale.gif index d00b910..bda4397 100644 Binary files a/docs/assets/resize_scale.gif and b/docs/assets/resize_scale.gif differ diff --git a/packages/box_transform/lib/src/geometry.dart b/packages/box_transform/lib/src/geometry.dart index b378aa0..8e25860 100644 --- a/packages/box_transform/lib/src/geometry.dart +++ b/packages/box_transform/lib/src/geometry.dart @@ -625,6 +625,9 @@ class Box { } /// Constrains the given [child] box instance within the bounds of this box. + /// This function will preserve the sign of the child's width and height. + /// It will also maintain the aspect ratio of the child if the [aspectRatio] + /// is specified. /// /// [child] the child box to clamp inside this box. /// [resizeMode] defines how to contain the child, whether it should keep its @@ -646,16 +649,21 @@ class Box { final double clampedLeft = math.min(x, right - childWidth); final double clampedTop = math.min(y, bottom - childHeight); - double newLeft = math.max(left, clampedLeft); - double newTop = math.max(top, clampedTop); + final double newLeft = math.max(left, clampedLeft); + final double newTop = math.max(top, clampedTop); double newWidth = math.min(width, childWidth); double newHeight = math.min(height, childHeight); + if (resizeMode.isScalable && aspectRatio != null) { final double newAspectRatio = newWidth / newHeight; + final double widthAdjustment = newHeight * aspectRatio; + final double heightAdjustment = newWidth / aspectRatio; if (aspectRatio < newAspectRatio) { + newHeight = math.min(height, heightAdjustment); newWidth = newHeight * aspectRatio; - } else { + } else if (aspectRatio > newAspectRatio) { + newWidth = math.min(width, widthAdjustment); newHeight = newWidth / aspectRatio; } } diff --git a/packages/flutter_box_transform/example/lib/main.dart b/packages/flutter_box_transform/example/lib/main.dart index bc2ad02..a3a6027 100644 --- a/packages/flutter_box_transform/example/lib/main.dart +++ b/packages/flutter_box_transform/example/lib/main.dart @@ -409,7 +409,7 @@ class _ImageBoxState extends State { resizable: model.resizable, hideHandlesWhenNotResizable: model.hideHandlesWhenNotResizable, movable: model.movable, - flipChild: model.flipChild, + allowContentFlipping: model.flipChild, flipWhileResizing: model.flipRectWhileResizing, onTerminalSizeReached: ( bool reachedMinWidth, @@ -429,7 +429,7 @@ class _ImageBoxState extends State { maxHeightReached = reachedMaxHeight; }); }, - childBuilder: (context, rect, flip) => Container( + contentBuilder: (context, rect, flip) => Container( width: rect.width, height: rect.height, decoration: BoxDecoration( @@ -557,7 +557,7 @@ class _ClampingRectState extends State { hasShadow: false, handleAlign: HandleAlign.inside, ), - childBuilder: (context, _, flip) => Container( + contentBuilder: (context, _, flip) => Container( width: model.clampingRect.width, height: model.clampingRect.height, alignment: Alignment.bottomRight, diff --git a/packages/flutter_box_transform/lib/src/transformable_box.dart b/packages/flutter_box_transform/lib/src/transformable_box.dart index 482ae70..7c34573 100644 --- a/packages/flutter_box_transform/lib/src/transformable_box.dart +++ b/packages/flutter_box_transform/lib/src/transformable_box.dart @@ -84,7 +84,7 @@ class TransformableBox extends StatefulWidget { /// [TransformableBox]. This is the physical widget you wish to show resizable /// handles on. It's most commonly something like an image widget, but it /// could be anything you want to have resizable & draggable box handles on. - final TransformableChildBuilder childBuilder; + final TransformableChildBuilder contentBuilder; /// A builder function that is used to build the corners handles of the /// [TransformableBox]. If you don't specify it, the default handles will be @@ -234,9 +234,10 @@ class TransformableBox extends StatefulWidget { /// corners of the rect. final bool flipWhileResizing; - /// Whether to flip the child of the box when the box is flipped. If this is - /// set to true, the child will be flipped when the box is flipped. - final bool flipChild; + /// Decides whether to flip the contents of the box when the box is flipped. + /// If this is set to true, the contents will be flipped when the box is + /// flipped. + final bool allowContentFlipping; /// How to align the handles. final HandleAlign handleAlign; @@ -244,7 +245,7 @@ class TransformableBox extends StatefulWidget { /// Creates a [TransformableBox] widget. const TransformableBox({ super.key, - required this.childBuilder, + required this.contentBuilder, this.onChanged, this.onMoved, this.onResized, @@ -252,13 +253,23 @@ class TransformableBox extends StatefulWidget { this.cornerHandleBuilder = _defaultCornerHandleBuilder, this.sideHandleBuilder = _defaultSideHandleBuilder, this.handleTapSize = 24, - // raw + + // Additional controls. + this.resizable = true, + this.movable = true, + this.flipWhileResizing = true, + this.allowContentFlipping = true, + this.handleAlign = HandleAlign.center, + this.hideHandlesWhenNotResizable = true, + + // Raw values. Rect? rect, Flip? flip, Rect? clampingRect, BoxConstraints? constraints, ResolveResizeModeCallback? resolveResizeModeCallback, - // terminal update events + + // Terminal update events. this.onMinWidthReached, this.onMaxWidthReached, this.onMinHeightReached, @@ -266,12 +277,6 @@ class TransformableBox extends StatefulWidget { this.onTerminalWidthReached, this.onTerminalHeightReached, this.onTerminalSizeReached, - this.resizable = true, - this.hideHandlesWhenNotResizable = true, - this.movable = true, - this.flipWhileResizing = true, - this.flipChild = true, - this.handleAlign = HandleAlign.center, }) : assert( controller == null || (rect == null && @@ -281,8 +286,8 @@ class TransformableBox extends StatefulWidget { resolveResizeModeCallback == null), 'You can either provide a [controller] OR a [box], [flip], ' '[clampingRect], [constraints], and [resolveResizeModeCallback]. ' - 'You cannot use any of those properties when providing a controller and' - 'vice versa.', + 'You cannot use any of those properties when providing a controller ' + 'and vice versa.', ), rect = rect ?? Rect.zero, flip = flip ?? Flip.none, @@ -317,7 +322,7 @@ class _TransformableBoxState extends State { ..movable = widget.movable ..resizable = widget.resizable ..flipWhileResizing = widget.flipWhileResizing - ..flipChild = widget.flipChild; + ..flipChild = widget.allowContentFlipping; } } @@ -344,7 +349,7 @@ class _TransformableBoxState extends State { ..hideHandlesWhenNotResizable = widget.hideHandlesWhenNotResizable ..movable = widget.movable ..flipWhileResizing = widget.flipWhileResizing - ..flipChild = widget.flipChild; + ..flipChild = widget.allowContentFlipping; } // Return if the controller is external. @@ -355,13 +360,16 @@ class _TransformableBoxState extends State { if (oldWidget.rect != widget.rect) { controller.rect = widget.rect; } + if (oldWidget.flip != widget.flip) { controller.flip = widget.flip; } + if (oldWidget.resolveResizeModeCallback != widget.resolveResizeModeCallback) { controller.resolveResizeModeCallback = widget.resolveResizeModeCallback; } + if (oldWidget.clampingRect != widget.clampingRect) { controller.clampingRect = widget.clampingRect; controller.recalculatePosition(notify: false); @@ -386,8 +394,8 @@ class _TransformableBoxState extends State { controller.movable = widget.movable; } - if (oldWidget.flipChild != widget.flipChild) { - controller.flipChild = widget.flipChild; + if (oldWidget.allowContentFlipping != widget.allowContentFlipping) { + controller.flipChild = widget.allowContentFlipping; } if (oldWidget.flipWhileResizing != widget.flipWhileResizing) { @@ -490,7 +498,7 @@ class _TransformableBoxState extends State { child: Transform.scale( scaleX: controller.flipChild && flip.isHorizontal ? -1 : 1, scaleY: controller.flipChild && flip.isVertical ? -1 : 1, - child: widget.childBuilder(context, box, flip), + child: widget.contentBuilder(context, box, flip), ), ), ),