-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Closed
Labels
P3Issues that are less important to the Flutter projectIssues that are less important to the Flutter projectfound in release: 3.10Found to occur in 3.10Found to occur in 3.10found in release: 3.12Found to occur in 3.12Found to occur in 3.12frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onr: fixedIssue is closed as already fixed in a newer versionIssue is closed as already fixed in a newer versionteam-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team
Description
Is there an existing issue for this?
- I have searched the existing issues
- I have read the guide to filing a bug
Steps to reproduce
Run the test
Expected results
Actual results
Firstly, when enableOverflowBox=false, the results are expected:
- When contentSuperLong=false, i.e. content is short, the test passes
- When contentSuperLong=true, i.e. content is too long and overflows, the test correctly prints an error saying "A RenderFlex overflowed by 9400 pixels on the bottom". That is expected, because our content is really too long.
Secondly, when enableOverflowBox=true, i.e. use OverflowBox, the results are less than optimal:
- When contentSuperLong=false, i.e. content is short, the test fails. This is the main point the PR is going to solve. Here,
OverflowBoxalways sets its ownsizeasconstraints.biggest, while users wants it to "shrink wrap", i.e. when child is small, the OverflowBox should be as small as child. - When contentSuperLong=true, i.e. content is too long and overflows, the OverflowBox does its job correctly.
In other words, the enableOverflowBox=true contentSuperLong=false needs to be fixed.
Code Fix
Please copy-paste the following MyOverflowBox, and modify usage of OverflowBox to MyOverflowBox.
Then, as can be seen, the enableOverflowBox=true contentSuperLong=false case now passes test, because it correctly shrink wraps.
Details
class MyOverflowBox extends SingleChildRenderObjectWidget {
/// Creates a widget that lets its child overflow itself.
const MyOverflowBox({
super.key,
this.alignment = Alignment.center,
this.minWidth,
this.maxWidth,
this.minHeight,
this.maxHeight,
super.child,
});
/// How to align the child.
///
/// The x and y values of the alignment control the horizontal and vertical
/// alignment, respectively. An x value of -1.0 means that the left edge of
/// the child is aligned with the left edge of the parent whereas an x value
/// of 1.0 means that the right edge of the child is aligned with the right
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
/// For example, a value of 0.0 means that the center of the child is aligned
/// with the center of the parent.
///
/// Defaults to [Alignment.center].
///
/// See also:
///
/// * [Alignment], a class with convenient constants typically used to
/// specify an [AlignmentGeometry].
/// * [AlignmentDirectional], like [Alignment] for specifying alignments
/// relative to text direction.
final AlignmentGeometry alignment;
/// The minimum width constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
final double? minWidth;
/// The maximum width constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
final double? maxWidth;
/// The minimum height constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
final double? minHeight;
/// The maximum height constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
final double? maxHeight;
@override
MyRenderConstrainedOverflowBox createRenderObject(BuildContext context) {
return MyRenderConstrainedOverflowBox(
alignment: alignment,
minWidth: minWidth,
maxWidth: maxWidth,
minHeight: minHeight,
maxHeight: maxHeight,
textDirection: Directionality.maybeOf(context),
);
}
@override
void updateRenderObject(BuildContext context, MyRenderConstrainedOverflowBox renderObject) {
renderObject
..alignment = alignment
..minWidth = minWidth
..maxWidth = maxWidth
..minHeight = minHeight
..maxHeight = maxHeight
..textDirection = Directionality.maybeOf(context);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
properties.add(DoubleProperty('minWidth', minWidth, defaultValue: null));
properties.add(DoubleProperty('maxWidth', maxWidth, defaultValue: null));
properties.add(DoubleProperty('minHeight', minHeight, defaultValue: null));
properties.add(DoubleProperty('maxHeight', maxHeight, defaultValue: null));
}
}
class MyRenderConstrainedOverflowBox extends RenderAligningShiftedBox {
/// Creates a render object that lets its child overflow itself.
MyRenderConstrainedOverflowBox({
super.child,
double? minWidth,
double? maxWidth,
double? minHeight,
double? maxHeight,
super.alignment,
super.textDirection,
}) : _minWidth = minWidth,
_maxWidth = maxWidth,
_minHeight = minHeight,
_maxHeight = maxHeight;
/// The minimum width constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
double? get minWidth => _minWidth;
double? _minWidth;
set minWidth(double? value) {
if (_minWidth == value) {
return;
}
_minWidth = value;
markNeedsLayout();
}
/// The maximum width constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
double? get maxWidth => _maxWidth;
double? _maxWidth;
set maxWidth(double? value) {
if (_maxWidth == value) {
return;
}
_maxWidth = value;
markNeedsLayout();
}
/// The minimum height constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
double? get minHeight => _minHeight;
double? _minHeight;
set minHeight(double? value) {
if (_minHeight == value) {
return;
}
_minHeight = value;
markNeedsLayout();
}
/// The maximum height constraint to give the child. Set this to null (the
/// default) to use the constraint from the parent instead.
double? get maxHeight => _maxHeight;
double? _maxHeight;
set maxHeight(double? value) {
if (_maxHeight == value) {
return;
}
_maxHeight = value;
markNeedsLayout();
}
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
return BoxConstraints(
minWidth: _minWidth ?? constraints.minWidth,
maxWidth: _maxWidth ?? constraints.maxWidth,
minHeight: _minHeight ?? constraints.minHeight,
maxHeight: _maxHeight ?? constraints.maxHeight,
);
}
@override
// bool get sizedByParent => true;
bool get sizedByParent => false; // NOTE MODIFIED
// NOTE MODIFIED
// @override
// Size computeDryLayout(BoxConstraints constraints) {
// return constraints.biggest;
// }
@override
void performLayout() {
if (child != null) {
child?.layout(_getInnerConstraints(constraints), parentUsesSize: true);
// alignChild(); // NOTE MODIFIED
}
size = child != null ? constraints.constrain(child!.size) : constraints.smallest; // NOTE MODIFIED
if (child != null) {
alignChild();
}
print('hi performLayout constraints=$constraints size=$size child.size=${child?.size}');
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DoubleProperty('minWidth', minWidth, ifNull: 'use parent minWidth constraint'));
properties.add(DoubleProperty('maxWidth', maxWidth, ifNull: 'use parent maxWidth constraint'));
properties.add(DoubleProperty('minHeight', minHeight, ifNull: 'use parent minHeight constraint'));
properties.add(DoubleProperty('maxHeight', maxHeight, ifNull: 'use parent maxHeight constraint'));
}
}
Code sample
Code sample
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
for (final contentSuperLong in [false, true]) {
for (final enableOverflowBox in [false, true]) {
testWidgets('contentSuperLong=$contentSuperLong enableOverflowBox=$enableOverflowBox',
(WidgetTester tester) async {
final key = GlobalKey();
final child = Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 100, height: contentSuperLong ? 10000 : 100),
],
);
await tester.pumpWidget(
MaterialApp(
home: Stack(
children: [
Container(
key: key,
child: enableOverflowBox
? OverflowBox(
maxHeight: 1000000,
child: child,
)
: child,
),
],
),
),
);
expect(tester.getBottomLeft(find.byKey(key)).dy, contentSuperLong ? 600 : 100);
});
}
}
}Screenshots or Video
Screenshots / Video demonstration
[Upload media here]
Logs
Logs
[Paste your logs here]Flutter Doctor output
Doctor output
[Paste your output here]Metadata
Metadata
Assignees
Labels
P3Issues that are less important to the Flutter projectIssues that are less important to the Flutter projectfound in release: 3.10Found to occur in 3.10Found to occur in 3.10found in release: 3.12Found to occur in 3.12Found to occur in 3.12frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onr: fixedIssue is closed as already fixed in a newer versionIssue is closed as already fixed in a newer versionteam-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team