Skip to content

Commit 5496a98

Browse files
committed
Fix bugs in RenderSizedOverflowBox and RenderFractionallySizedOverflowBox
Bug #1: These didn't work with directional alignments, due to an error in the types of the constructor arguments. Bug #2: Pretty sure RenderSizedOverflowBox never worked at all. As soon as I wrote a test for it, the test showed that there was a fundamental bug in its performLayout method: it didn't set parentUsesSize, but it immediately tried to use the child's size.
1 parent 2f63044 commit 5496a98

File tree

3 files changed

+100
-5
lines changed

3 files changed

+100
-5
lines changed

packages/flutter/lib/src/rendering/shifted_box.dart

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
229229
/// Initializes member variables for subclasses.
230230
///
231231
/// The [alignment] argument must not be null.
232+
///
233+
/// The [textDirection] must be non-null if the [alignment] is
234+
/// direction-sensitive.
232235
RenderAligningShiftedBox({
233236
AlignmentGeometry alignment = Alignment.center,
234237
@required TextDirection textDirection,
@@ -303,6 +306,7 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
303306
///
304307
/// This method must be called after the child has been laid out and
305308
/// this object's own size has been set.
309+
@protected
306310
void alignChild() {
307311
_resolve();
308312
assert(child != null);
@@ -707,6 +711,9 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow
707711
/// A render object that is a specific size but passes its original constraints
708712
/// through to its child, which it allows to overflow.
709713
///
714+
/// If the child's resulting size differs from this render object's size, then
715+
/// the child is aligned according to the [alignment] property.
716+
///
710717
/// See also:
711718
/// * [RenderUnconstrainedBox] for a render object that allows its children
712719
/// to render themselves unconstrained, expands to fit them, and considers
@@ -717,11 +724,14 @@ class RenderUnconstrainedBox extends RenderAligningShiftedBox with DebugOverflow
717724
class RenderSizedOverflowBox extends RenderAligningShiftedBox {
718725
/// Creates a render box of a given size that lets its child overflow.
719726
///
720-
/// The [requestedSize] argument must not be null.
727+
/// The [requestedSize] and [alignment] arguments must not be null.
728+
///
729+
/// The [textDirection] argument must not be null if the [alignment] is
730+
/// direction-sensitive.
721731
RenderSizedOverflowBox({
722732
RenderBox child,
723733
@required Size requestedSize,
724-
Alignment alignment = Alignment.center,
734+
AlignmentGeometry alignment = Alignment.center,
725735
TextDirection textDirection,
726736
}) : assert(requestedSize != null),
727737
_requestedSize = requestedSize,
@@ -769,7 +779,7 @@ class RenderSizedOverflowBox extends RenderAligningShiftedBox {
769779
void performLayout() {
770780
size = constraints.constrain(_requestedSize);
771781
if (child != null) {
772-
child.layout(constraints);
782+
child.layout(constraints, parentUsesSize: true);
773783
alignChild();
774784
}
775785
}
@@ -783,17 +793,24 @@ class RenderSizedOverflowBox extends RenderAligningShiftedBox {
783793
/// for a given axis is null, then the constraints from the parent are just
784794
/// passed through instead.
785795
///
786-
/// It then tries to size itself to the size of its child.
796+
/// It then tries to size itself to the size of its child. Where this is not
797+
/// possible (e.g. if the constraints from the parent are themselves tight), the
798+
/// child is aligned according to [alignment].
787799
class RenderFractionallySizedOverflowBox extends RenderAligningShiftedBox {
788800
/// Creates a render box that sizes its child to a fraction of the total available space.
789801
///
790802
/// If non-null, the [widthFactor] and [heightFactor] arguments must be
791803
/// non-negative.
804+
///
805+
/// The [alignment] must not be null.
806+
///
807+
/// The [textDirection] must be non-null if the [alignment] is
808+
/// direction-sensitive.
792809
RenderFractionallySizedOverflowBox({
793810
RenderBox child,
794811
double widthFactor,
795812
double heightFactor,
796-
Alignment alignment = Alignment.center,
813+
AlignmentGeometry alignment = Alignment.center,
797814
TextDirection textDirection,
798815
}) : _widthFactor = widthFactor,
799816
_heightFactor = heightFactor,

packages/flutter/test/widgets/fractionally_sized_box_test.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,36 @@ void main() {
2929
expect(box.size, equals(const Size(50.0, 25.0)));
3030
expect(box.localToGlobal(Offset.zero), equals(const Offset(25.0, 37.5)));
3131
});
32+
33+
testWidgets('FractionallySizedBox alignment', (WidgetTester tester) async {
34+
final GlobalKey inner = GlobalKey();
35+
await tester.pumpWidget(Directionality(
36+
textDirection: TextDirection.rtl,
37+
child: FractionallySizedBox(
38+
widthFactor: 0.5,
39+
heightFactor: 0.5,
40+
alignment: Alignment.topRight,
41+
child: Placeholder(key: inner),
42+
),
43+
));
44+
final RenderBox box = inner.currentContext.findRenderObject();
45+
expect(box.size, equals(const Size(400.0, 300.0)));
46+
expect(box.localToGlobal(box.size.center(Offset.zero)), equals(const Offset(800.0 - 400.0 / 2.0, 0.0 + 300.0 / 2.0)));
47+
});
48+
49+
testWidgets('FractionallySizedBox alignment (direction-sensitive)', (WidgetTester tester) async {
50+
final GlobalKey inner = GlobalKey();
51+
await tester.pumpWidget(Directionality(
52+
textDirection: TextDirection.rtl,
53+
child: FractionallySizedBox(
54+
widthFactor: 0.5,
55+
heightFactor: 0.5,
56+
alignment: AlignmentDirectional.topEnd,
57+
child: Placeholder(key: inner),
58+
),
59+
));
60+
final RenderBox box = inner.currentContext.findRenderObject();
61+
expect(box.size, equals(const Size(400.0, 300.0)));
62+
expect(box.localToGlobal(box.size.center(Offset.zero)), equals(const Offset(0.0 + 400.0 / 2.0, 0.0 + 300.0 / 2.0)));
63+
});
3264
}

packages/flutter/test/widgets/overflow_box_test.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,50 @@ void main() {
4949
'maxHeight: 4.0',
5050
]);
5151
});
52+
53+
testWidgets('SizedOverflowBox alignment', (WidgetTester tester) async {
54+
final GlobalKey inner = GlobalKey();
55+
await tester.pumpWidget(Directionality(
56+
textDirection: TextDirection.rtl,
57+
child: Center(
58+
child: SizedOverflowBox(
59+
size: const Size(100.0, 100.0),
60+
alignment: Alignment.topRight,
61+
child: Container(height: 50.0, width: 50.0, key: inner),
62+
),
63+
),
64+
));
65+
final RenderBox box = inner.currentContext.findRenderObject();
66+
expect(box.size, equals(const Size(50.0, 50.0)));
67+
expect(
68+
box.localToGlobal(box.size.center(Offset.zero)),
69+
equals(const Offset(
70+
(800.0 - 100.0) / 2.0 + 100.0 - 50.0 / 2.0,
71+
(600.0 - 100.0) / 2.0 + 0.0 + 50.0 / 2.0,
72+
)),
73+
);
74+
});
75+
76+
testWidgets('SizedOverflowBox alignment (direction-sensitive)', (WidgetTester tester) async {
77+
final GlobalKey inner = GlobalKey();
78+
await tester.pumpWidget(Directionality(
79+
textDirection: TextDirection.rtl,
80+
child: Center(
81+
child: SizedOverflowBox(
82+
size: const Size(100.0, 100.0),
83+
alignment: AlignmentDirectional.bottomStart,
84+
child: Container(height: 50.0, width: 50.0, key: inner),
85+
),
86+
),
87+
));
88+
final RenderBox box = inner.currentContext.findRenderObject();
89+
expect(box.size, equals(const Size(50.0, 50.0)));
90+
expect(
91+
box.localToGlobal(box.size.center(Offset.zero)),
92+
equals(const Offset(
93+
(800.0 - 100.0) / 2.0 + 100.0 - 50.0 / 2.0,
94+
(600.0 - 100.0) / 2.0 + 100.0 - 50.0 / 2.0,
95+
)),
96+
);
97+
});
5298
}

0 commit comments

Comments
 (0)