diff --git a/packages/flutter/lib/src/rendering/shifted_box.dart b/packages/flutter/lib/src/rendering/shifted_box.dart index f9a181aa9e..c9b74ac721 100644 --- a/packages/flutter/lib/src/rendering/shifted_box.dart +++ b/packages/flutter/lib/src/rendering/shifted_box.dart @@ -229,6 +229,9 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox { /// Initializes member variables for subclasses. /// /// The [alignment] argument must not be null. + /// + /// The [textDirection] must be non-null if the [alignment] is + /// direction-sensitive. RenderAligningShiftedBox({ AlignmentGeometry alignment = Alignment.center, @required TextDirection textDirection, @@ -303,6 +306,7 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox { /// /// This method must be called after the child has been laid out and /// this object's own size has been set. + @protected void alignChild() { _resolve(); assert(child != null); @@ -707,6 +711,9 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow /// A render object that is a specific size but passes its original constraints /// through to its child, which it allows to overflow. /// +/// If the child's resulting size differs from this render object's size, then +/// the child is aligned according to the [alignment] property. +/// /// See also: /// * [RenderUnconstrainedBox] for a render object that allows its children /// to render themselves unconstrained, expands to fit them, and considers @@ -717,11 +724,14 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow class RenderSizedOverflowBox extends RenderAligningShiftedBox { /// Creates a render box of a given size that lets its child overflow. /// - /// The [requestedSize] argument must not be null. + /// The [requestedSize] and [alignment] arguments must not be null. + /// + /// The [textDirection] argument must not be null if the [alignment] is + /// direction-sensitive. RenderSizedOverflowBox({ RenderBox child, @required Size requestedSize, - Alignment alignment = Alignment.center, + AlignmentGeometry alignment = Alignment.center, TextDirection textDirection, }) : assert(requestedSize != null), _requestedSize = requestedSize, @@ -769,7 +779,7 @@ class RenderSizedOverflowBox extends RenderAligningShiftedBox { void performLayout() { size = constraints.constrain(_requestedSize); if (child != null) { - child.layout(constraints); + child.layout(constraints, parentUsesSize: true); alignChild(); } } @@ -783,17 +793,24 @@ class RenderSizedOverflowBox extends RenderAligningShiftedBox { /// for a given axis is null, then the constraints from the parent are just /// passed through instead. /// -/// It then tries to size itself to the size of its child. +/// It then tries to size itself to the size of its child. Where this is not +/// possible (e.g. if the constraints from the parent are themselves tight), the +/// child is aligned according to [alignment]. class RenderFractionallySizedOverflowBox extends RenderAligningShiftedBox { /// Creates a render box that sizes its child to a fraction of the total available space. /// /// If non-null, the [widthFactor] and [heightFactor] arguments must be /// non-negative. + /// + /// The [alignment] must not be null. + /// + /// The [textDirection] must be non-null if the [alignment] is + /// direction-sensitive. RenderFractionallySizedOverflowBox({ RenderBox child, double widthFactor, double heightFactor, - Alignment alignment = Alignment.center, + AlignmentGeometry alignment = Alignment.center, TextDirection textDirection, }) : _widthFactor = widthFactor, _heightFactor = heightFactor, diff --git a/packages/flutter/test/widgets/fractionally_sized_box_test.dart b/packages/flutter/test/widgets/fractionally_sized_box_test.dart index 2feb6b5046..006572e71c 100644 --- a/packages/flutter/test/widgets/fractionally_sized_box_test.dart +++ b/packages/flutter/test/widgets/fractionally_sized_box_test.dart @@ -29,4 +29,36 @@ void main() { expect(box.size, equals(const Size(50.0, 25.0))); expect(box.localToGlobal(Offset.zero), equals(const Offset(25.0, 37.5))); }); + + testWidgets('FractionallySizedBox alignment', (WidgetTester tester) async { + final GlobalKey inner = GlobalKey(); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.rtl, + child: FractionallySizedBox( + widthFactor: 0.5, + heightFactor: 0.5, + alignment: Alignment.topRight, + child: Placeholder(key: inner), + ), + )); + final RenderBox box = inner.currentContext.findRenderObject(); + expect(box.size, equals(const Size(400.0, 300.0))); + expect(box.localToGlobal(box.size.center(Offset.zero)), equals(const Offset(800.0 - 400.0 / 2.0, 0.0 + 300.0 / 2.0))); + }); + + testWidgets('FractionallySizedBox alignment (direction-sensitive)', (WidgetTester tester) async { + final GlobalKey inner = GlobalKey(); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.rtl, + child: FractionallySizedBox( + widthFactor: 0.5, + heightFactor: 0.5, + alignment: AlignmentDirectional.topEnd, + child: Placeholder(key: inner), + ), + )); + final RenderBox box = inner.currentContext.findRenderObject(); + expect(box.size, equals(const Size(400.0, 300.0))); + expect(box.localToGlobal(box.size.center(Offset.zero)), equals(const Offset(0.0 + 400.0 / 2.0, 0.0 + 300.0 / 2.0))); + }); } diff --git a/packages/flutter/test/widgets/overflow_box_test.dart b/packages/flutter/test/widgets/overflow_box_test.dart index fbfb0fa2ae..e6c0517df5 100644 --- a/packages/flutter/test/widgets/overflow_box_test.dart +++ b/packages/flutter/test/widgets/overflow_box_test.dart @@ -49,4 +49,50 @@ void main() { 'maxHeight: 4.0', ]); }); + + testWidgets('SizedOverflowBox alignment', (WidgetTester tester) async { + final GlobalKey inner = GlobalKey(); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.rtl, + child: Center( + child: SizedOverflowBox( + size: const Size(100.0, 100.0), + alignment: Alignment.topRight, + child: Container(height: 50.0, width: 50.0, key: inner), + ), + ), + )); + final RenderBox box = inner.currentContext.findRenderObject(); + expect(box.size, equals(const Size(50.0, 50.0))); + expect( + box.localToGlobal(box.size.center(Offset.zero)), + equals(const Offset( + (800.0 - 100.0) / 2.0 + 100.0 - 50.0 / 2.0, + (600.0 - 100.0) / 2.0 + 0.0 + 50.0 / 2.0, + )), + ); + }); + + testWidgets('SizedOverflowBox alignment (direction-sensitive)', (WidgetTester tester) async { + final GlobalKey inner = GlobalKey(); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.rtl, + child: Center( + child: SizedOverflowBox( + size: const Size(100.0, 100.0), + alignment: AlignmentDirectional.bottomStart, + child: Container(height: 50.0, width: 50.0, key: inner), + ), + ), + )); + final RenderBox box = inner.currentContext.findRenderObject(); + expect(box.size, equals(const Size(50.0, 50.0))); + expect( + box.localToGlobal(box.size.center(Offset.zero)), + equals(const Offset( + (800.0 - 100.0) / 2.0 + 100.0 - 50.0 / 2.0, + (600.0 - 100.0) / 2.0 + 100.0 - 50.0 / 2.0, + )), + ); + }); }