Skip to content

Commit 6df6c89

Browse files
authored
Let OverflowBox be shrink-wrappable (flutter#129095)
Close flutter#129094 I have demonstrated how this PR fixes the problem using tests in flutter#129094. I will further add tests in this PR if the PR looks roughly acceptable :)
1 parent dcbb2de commit 6df6c89

File tree

4 files changed

+142
-4
lines changed

4 files changed

+142
-4
lines changed

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

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,20 @@ class RenderPositionedBox extends RenderAligningShiftedBox {
533533
}
534534
}
535535

536+
/// How much space should be occupied by the [OverflowBox] if there is no
537+
/// overflow.
538+
enum OverflowBoxFit {
539+
/// The widget will size itself to be as large as the parent allows.
540+
max,
541+
542+
/// The widget will follow the child's size.
543+
///
544+
/// More specifically, the render object will size itself to match the size of
545+
/// its child within the constraints of its parent, or as small as the
546+
/// parent allows if no child is set.
547+
deferToChild,
548+
}
549+
536550
/// A render object that imposes different constraints on its child than it gets
537551
/// from its parent, possibly allowing the child to overflow the parent.
538552
///
@@ -571,12 +585,14 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
571585
double? maxWidth,
572586
double? minHeight,
573587
double? maxHeight,
588+
OverflowBoxFit fit = OverflowBoxFit.max,
574589
super.alignment,
575590
super.textDirection,
576591
}) : _minWidth = minWidth,
577592
_maxWidth = maxWidth,
578593
_minHeight = minHeight,
579-
_maxHeight = maxHeight;
594+
_maxHeight = maxHeight,
595+
_fit = fit;
580596

581597
/// The minimum width constraint to give the child. Set this to null (the
582598
/// default) to use the constraint from the parent instead.
@@ -626,6 +642,24 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
626642
markNeedsLayout();
627643
}
628644

645+
/// The way to size the render object.
646+
///
647+
/// This only affects scenario when the child does not indeed overflow.
648+
/// If set to [OverflowBoxFit.deferToChild], the render object will size
649+
/// itself to match the size of its child within the constraints of its
650+
/// parent, or as small as the parent allows if no child is set.
651+
/// If set to [OverflowBoxFit.max] (the default), the
652+
/// render object will size itself to be as large as the parent allows.
653+
OverflowBoxFit get fit => _fit;
654+
OverflowBoxFit _fit;
655+
set fit(OverflowBoxFit value) {
656+
if (_fit == value) {
657+
return;
658+
}
659+
_fit = value;
660+
markNeedsLayoutForSizedByParentChange();
661+
}
662+
629663
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
630664
return BoxConstraints(
631665
minWidth: _minWidth ?? constraints.minWidth,
@@ -636,19 +670,46 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
636670
}
637671

638672
@override
639-
bool get sizedByParent => true;
673+
bool get sizedByParent {
674+
switch (fit) {
675+
case OverflowBoxFit.max:
676+
return true;
677+
case OverflowBoxFit.deferToChild:
678+
// If deferToChild, the size will be as small as its child when non-overflowing,
679+
// thus it cannot be sizedByParent.
680+
return false;
681+
}
682+
}
640683

641684
@override
642685
@protected
643686
Size computeDryLayout(covariant BoxConstraints constraints) {
644-
return constraints.biggest;
687+
switch (fit) {
688+
case OverflowBoxFit.max:
689+
return constraints.biggest;
690+
case OverflowBoxFit.deferToChild:
691+
return child?.getDryLayout(constraints) ?? constraints.smallest;
692+
}
645693
}
646694

647695
@override
648696
void performLayout() {
649697
if (child != null) {
650-
child?.layout(_getInnerConstraints(constraints), parentUsesSize: true);
698+
child!.layout(_getInnerConstraints(constraints), parentUsesSize: true);
699+
switch (fit) {
700+
case OverflowBoxFit.max:
701+
assert(sizedByParent);
702+
case OverflowBoxFit.deferToChild:
703+
size = constraints.constrain(child!.size);
704+
}
651705
alignChild();
706+
} else {
707+
switch (fit) {
708+
case OverflowBoxFit.max:
709+
assert(sizedByParent);
710+
case OverflowBoxFit.deferToChild:
711+
size = constraints.smallest;
712+
}
652713
}
653714
}
654715

@@ -659,6 +720,7 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
659720
properties.add(DoubleProperty('maxWidth', maxWidth, ifNull: 'use parent maxWidth constraint'));
660721
properties.add(DoubleProperty('minHeight', minHeight, ifNull: 'use parent minHeight constraint'));
661722
properties.add(DoubleProperty('maxHeight', maxHeight, ifNull: 'use parent maxHeight constraint'));
723+
properties.add(EnumProperty<OverflowBoxFit>('fit', fit));
662724
}
663725
}
664726

packages/flutter/lib/src/widgets/basic.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3051,6 +3051,7 @@ class OverflowBox extends SingleChildRenderObjectWidget {
30513051
this.maxWidth,
30523052
this.minHeight,
30533053
this.maxHeight,
3054+
this.fit = OverflowBoxFit.max,
30543055
super.child,
30553056
});
30563057

@@ -3090,6 +3091,16 @@ class OverflowBox extends SingleChildRenderObjectWidget {
30903091
/// default) to use the constraint from the parent instead.
30913092
final double? maxHeight;
30923093

3094+
/// The way to size the render object.
3095+
///
3096+
/// This only affects scenario when the child does not indeed overflow.
3097+
/// If set to [OverflowBoxFit.deferToChild], the render object will size itself to
3098+
/// match the size of its child within the constraints of its parent or be
3099+
/// as small as the parent allows if no child is set. If set to
3100+
/// [OverflowBoxFit.max] (the default), the render object will size itself
3101+
/// to be as large as the parent allows.
3102+
final OverflowBoxFit fit;
3103+
30933104
@override
30943105
RenderConstrainedOverflowBox createRenderObject(BuildContext context) {
30953106
return RenderConstrainedOverflowBox(
@@ -3098,6 +3109,7 @@ class OverflowBox extends SingleChildRenderObjectWidget {
30983109
maxWidth: maxWidth,
30993110
minHeight: minHeight,
31003111
maxHeight: maxHeight,
3112+
fit: fit,
31013113
textDirection: Directionality.maybeOf(context),
31023114
);
31033115
}
@@ -3110,6 +3122,7 @@ class OverflowBox extends SingleChildRenderObjectWidget {
31103122
..maxWidth = maxWidth
31113123
..minHeight = minHeight
31123124
..maxHeight = maxHeight
3125+
..fit = fit
31133126
..textDirection = Directionality.maybeOf(context);
31143127
}
31153128

@@ -3121,6 +3134,7 @@ class OverflowBox extends SingleChildRenderObjectWidget {
31213134
properties.add(DoubleProperty('maxWidth', maxWidth, defaultValue: null));
31223135
properties.add(DoubleProperty('minHeight', minHeight, defaultValue: null));
31233136
properties.add(DoubleProperty('maxHeight', maxHeight, defaultValue: null));
3137+
properties.add(EnumProperty<OverflowBoxFit>('fit', fit));
31243138
}
31253139
}
31263140

packages/flutter/test/rendering/limited_box_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ void main() {
4242
' │ maxWidth: Infinity\n'
4343
' │ minHeight: 0.0\n'
4444
' │ maxHeight: Infinity\n'
45+
' │ fit: max\n'
4546
' │\n'
4647
' └─child: RenderLimitedBox#00000 relayoutBoundary=up1 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE\n'
4748
' │ parentData: offset=Offset(350.0, 200.0) (can use size)\n'
@@ -128,6 +129,7 @@ void main() {
128129
' │ maxWidth: 500.0\n'
129130
' │ minHeight: 0.0\n'
130131
' │ maxHeight: Infinity\n'
132+
' │ fit: max\n'
131133
' │\n'
132134
' └─child: RenderLimitedBox#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
133135
' parentData: offset=Offset(395.0, 300.0) (can use size)\n'
@@ -164,6 +166,7 @@ void main() {
164166
' │ maxWidth: use parent maxWidth constraint\n'
165167
' │ minHeight: use parent minHeight constraint\n'
166168
' │ maxHeight: use parent maxHeight constraint\n'
169+
' │ fit: max\n'
167170
' │\n'
168171
' └─child: RenderLimitedBox#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
169172
' parentData: offset=Offset(395.0, 0.0) (can use size)\n'

packages/flutter/test/widgets/overflow_box_test.dart

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,64 @@ void main() {
3131
expect(box.size, equals(const Size(100.0, 50.0)));
3232
});
3333

34+
// Adapted from https://github.com/flutter/flutter/issues/129094
35+
group('when fit is OverflowBoxFit.deferToChild', () {
36+
group('OverflowBox behavior with long and short content', () {
37+
for (final bool contentSuperLong in <bool>[false, true]) {
38+
testWidgetsWithLeakTracking('contentSuperLong=$contentSuperLong', (WidgetTester tester) async {
39+
final GlobalKey<State<StatefulWidget>> key = GlobalKey();
40+
41+
final Column child = Column(
42+
mainAxisSize: MainAxisSize.min,
43+
children: <Widget>[
44+
SizedBox(width: 100, height: contentSuperLong ? 10000 : 100),
45+
],
46+
);
47+
48+
await tester.pumpWidget(Directionality(
49+
textDirection: TextDirection.ltr,
50+
child: Stack(
51+
children: <Widget>[
52+
Container(
53+
key: key,
54+
child: OverflowBox(
55+
maxHeight: 1000000,
56+
fit: OverflowBoxFit.deferToChild,
57+
child: child,
58+
),
59+
),
60+
],
61+
),
62+
));
63+
64+
expect(tester.getBottomLeft(find.byKey(key)).dy, contentSuperLong ? 600 : 100);
65+
});
66+
}
67+
});
68+
69+
testWidgetsWithLeakTracking('no child', (WidgetTester tester) async {
70+
final GlobalKey<State<StatefulWidget>> key = GlobalKey();
71+
72+
await tester.pumpWidget(Directionality(
73+
textDirection: TextDirection.ltr,
74+
child: Stack(
75+
children: <Widget>[
76+
Container(
77+
key: key,
78+
child: const OverflowBox(
79+
maxHeight: 1000000,
80+
fit: OverflowBoxFit.deferToChild,
81+
// no child
82+
),
83+
),
84+
],
85+
),
86+
));
87+
88+
expect(tester.getBottomLeft(find.byKey(key)).dy, 0);
89+
});
90+
});
91+
3492
testWidgetsWithLeakTracking('OverflowBox implements debugFillProperties', (WidgetTester tester) async {
3593
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
3694
const OverflowBox(
@@ -48,6 +106,7 @@ void main() {
48106
'maxWidth: 2.0',
49107
'minHeight: 3.0',
50108
'maxHeight: 4.0',
109+
'fit: max',
51110
]);
52111
});
53112

0 commit comments

Comments
 (0)