computeDryBaseline for rendering / widgets RenderBoxes (#146143)

RenderWrap, Table, Overlay / Stack are not included
This commit is contained in:
LongCatIsLooong 2024-04-03 11:36:19 -07:00 committed by GitHub
parent 99874c1c57
commit ef7019e801
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 321 additions and 212 deletions

View File

@ -4,7 +4,7 @@
import 'dart:collection'; import 'dart:collection';
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle, LineMetrics, PlaceholderAlignment, TextBox; import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle, LineMetrics, TextBox;
import 'package:characters/characters.dart'; import 'package:characters/characters.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -777,7 +777,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_textPainter.text = value; _textPainter.text = value;
_cachedAttributedValue = null; _cachedAttributedValue = null;
_cachedCombinedSemanticsInfos = null; _cachedCombinedSemanticsInfos = null;
_canComputeIntrinsicsCached = null;
markNeedsLayout(); markNeedsLayout();
markNeedsSemanticsUpdate(); markNeedsSemanticsUpdate();
} }
@ -1828,10 +1827,11 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
@override @override
double computeMinIntrinsicWidth(double height) { double computeMinIntrinsicWidth(double height) {
if (!_canComputeIntrinsics) { final List<PlaceholderDimensions> placeholderDimensions = layoutInlineChildren(
return 0.0; double.infinity,
} (RenderBox child, BoxConstraints constraints) => Size(child.getMinIntrinsicWidth(double.infinity), 0.0),
final List<PlaceholderDimensions> placeholderDimensions = layoutInlineChildren(double.infinity, (RenderBox child, BoxConstraints constraints) => Size(child.getMinIntrinsicWidth(double.infinity), 0.0)); ChildLayoutHelper.getDryBaseline,
);
final (double minWidth, double maxWidth) = _adjustConstraints(); final (double minWidth, double maxWidth) = _adjustConstraints();
return (_textIntrinsics return (_textIntrinsics
..setPlaceholderDimensions(placeholderDimensions) ..setPlaceholderDimensions(placeholderDimensions)
@ -1841,14 +1841,12 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) {
if (!_canComputeIntrinsics) {
return 0.0;
}
final List<PlaceholderDimensions> placeholderDimensions = layoutInlineChildren( final List<PlaceholderDimensions> placeholderDimensions = layoutInlineChildren(
double.infinity, double.infinity,
// Height and baseline is irrelevant as all text will be laid // Height and baseline is irrelevant as all text will be laid
// out in a single line. Therefore, using 0.0 as a dummy for the height. // out in a single line. Therefore, using 0.0 as a dummy for the height.
(RenderBox child, BoxConstraints constraints) => Size(child.getMaxIntrinsicWidth(double.infinity), 0.0), (RenderBox child, BoxConstraints constraints) => Size(child.getMaxIntrinsicWidth(double.infinity), 0.0),
ChildLayoutHelper.getDryBaseline,
); );
final (double minWidth, double maxWidth) = _adjustConstraints(); final (double minWidth, double maxWidth) = _adjustConstraints();
return (_textIntrinsics return (_textIntrinsics
@ -1926,10 +1924,9 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
@override @override
double computeMaxIntrinsicHeight(double width) { double computeMaxIntrinsicHeight(double width) {
if (!_canComputeIntrinsics) { _textIntrinsics.setPlaceholderDimensions(
return 0.0; layoutInlineChildren(width, ChildLayoutHelper.dryLayoutChild, ChildLayoutHelper.getDryBaseline),
} );
_textIntrinsics.setPlaceholderDimensions(layoutInlineChildren(width, ChildLayoutHelper.dryLayoutChild));
return _preferredHeight(width); return _preferredHeight(width);
} }
@ -2291,35 +2288,12 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
); );
} }
bool _canComputeDryLayoutForInlineWidgets() {
return text?.visitChildren((InlineSpan span) {
return (span is! PlaceholderSpan) || switch (span.alignment) {
ui.PlaceholderAlignment.baseline ||
ui.PlaceholderAlignment.aboveBaseline ||
ui.PlaceholderAlignment.belowBaseline => false,
ui.PlaceholderAlignment.top ||
ui.PlaceholderAlignment.middle ||
ui.PlaceholderAlignment.bottom => true,
};
}) ?? true;
}
bool? _canComputeIntrinsicsCached;
bool get _canComputeIntrinsics => _canComputeIntrinsicsCached ??= _canComputeDryLayoutForInlineWidgets();
@override @override
@protected @protected
Size computeDryLayout(covariant BoxConstraints constraints) { Size computeDryLayout(covariant BoxConstraints constraints) {
if (!_canComputeIntrinsics) {
assert(debugCannotComputeDryLayout(
reason: 'Dry layout not available for alignments that require baseline.',
));
return Size.zero;
}
final (double minWidth, double maxWidth) = _adjustConstraints(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth); final (double minWidth, double maxWidth) = _adjustConstraints(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
_textIntrinsics _textIntrinsics
..setPlaceholderDimensions(layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.dryLayoutChild)) ..setPlaceholderDimensions(layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.dryLayoutChild, ChildLayoutHelper.getDryBaseline))
..layout(minWidth: minWidth, maxWidth: maxWidth); ..layout(minWidth: minWidth, maxWidth: maxWidth);
final double width = forceLine final double width = forceLine
? constraints.maxWidth ? constraints.maxWidth
@ -2330,7 +2304,7 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
@override @override
void performLayout() { void performLayout() {
final BoxConstraints constraints = this.constraints; final BoxConstraints constraints = this.constraints;
_placeholderDimensions = layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.layoutChild); _placeholderDimensions = layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.layoutChild, ChildLayoutHelper.getBaseline);
final (double minWidth, double maxWidth) = _adjustConstraints(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth); final (double minWidth, double maxWidth) = _adjustConstraints(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
_textPainter _textPainter
..setPlaceholderDimensions(_placeholderDimensions) ..setPlaceholderDimensions(_placeholderDimensions)

View File

@ -63,6 +63,39 @@ class RenderListBody extends RenderBox
/// [axisDirection]. /// [axisDirection].
Axis get mainAxis => axisDirectionToAxis(axisDirection); Axis get mainAxis => axisDirectionToAxis(axisDirection);
@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
assert(_debugCheckConstraints(constraints));
RenderBox? child;
final RenderBox? Function(RenderBox) nextChild;
switch (axisDirection) {
case AxisDirection.right:
case AxisDirection.left:
final BoxConstraints childConstraints = BoxConstraints.tightFor(height: constraints.maxHeight);
BaselineOffset baselineOffset = BaselineOffset.noBaseline;
for (child = firstChild; child != null; child = childAfter(child)) {
baselineOffset = baselineOffset.minOf(BaselineOffset(child.getDryBaseline(childConstraints, baseline)));
}
return baselineOffset.offset;
case AxisDirection.up:
child = lastChild;
nextChild = childBefore;
case AxisDirection.down:
child = firstChild;
nextChild = childAfter;
}
final BoxConstraints childConstraints = BoxConstraints.tightFor(width: constraints.maxWidth);
double mainAxisExtent = 0.0;
for (; child != null; child = nextChild(child)) {
final double? childBaseline = child.getDryBaseline(childConstraints, baseline);
if (childBaseline != null) {
return childBaseline + mainAxisExtent;
}
mainAxisExtent += child.getDryLayout(childConstraints).height;
}
return null;
}
@override @override
@protected @protected
Size computeDryLayout(covariant BoxConstraints constraints) { Size computeDryLayout(covariant BoxConstraints constraints) {

View File

@ -125,14 +125,14 @@ mixin RenderInlineChildrenContainerDefaults on RenderBox, ContainerRenderObjectM
} }
} }
static PlaceholderDimensions _layoutChild(RenderBox child, double maxWidth, ChildLayouter layoutChild) { static PlaceholderDimensions _layoutChild(RenderBox child, BoxConstraints childConstraints, ChildLayouter layoutChild, ChildBaselineGetter getBaseline) {
final TextParentData parentData = child.parentData! as TextParentData; final TextParentData parentData = child.parentData! as TextParentData;
final PlaceholderSpan? span = parentData.span; final PlaceholderSpan? span = parentData.span;
assert(span != null); assert(span != null);
return span == null return span == null
? PlaceholderDimensions.empty ? PlaceholderDimensions.empty
: PlaceholderDimensions( : PlaceholderDimensions(
size: layoutChild(child, BoxConstraints(maxWidth: maxWidth)), size: layoutChild(child, childConstraints),
alignment: span.alignment, alignment: span.alignment,
baseline: span.baseline, baseline: span.baseline,
baselineOffset: switch (span.alignment) { baselineOffset: switch (span.alignment) {
@ -141,17 +141,21 @@ mixin RenderInlineChildrenContainerDefaults on RenderBox, ContainerRenderObjectM
ui.PlaceholderAlignment.bottom || ui.PlaceholderAlignment.bottom ||
ui.PlaceholderAlignment.middle || ui.PlaceholderAlignment.middle ||
ui.PlaceholderAlignment.top => null, ui.PlaceholderAlignment.top => null,
ui.PlaceholderAlignment.baseline => child.getDistanceToBaseline(span.baseline!), ui.PlaceholderAlignment.baseline => getBaseline(child, childConstraints, span.baseline!),
}, },
); );
} }
/// Computes the layout for every inline child using the given `layoutChild` /// Computes the layout for every inline child using the `maxWidth` constraint.
/// function and the `maxWidth` constraint.
/// ///
/// Returns a list of [PlaceholderDimensions], representing the layout results /// Returns a list of [PlaceholderDimensions], representing the layout results
/// for each child managed by the [ContainerRenderObjectMixin] mixin. /// for each child managed by the [ContainerRenderObjectMixin] mixin.
/// ///
/// The `getChildBaseline` parameter and the `layoutChild` parameter must be
/// consistent: if `layoutChild` computes the size of the child without
/// modifying the actual layout of that child, then `getChildBaseline` must
/// also be "dry", and vice versa.
///
/// Since this method does not impose a maximum height constraint on the /// Since this method does not impose a maximum height constraint on the
/// inline children, some children may become taller than this [RenderBox]. /// inline children, some children may become taller than this [RenderBox].
/// ///
@ -160,10 +164,11 @@ mixin RenderInlineChildrenContainerDefaults on RenderBox, ContainerRenderObjectM
/// * [TextPainter.setPlaceholderDimensions], the method that usually takes /// * [TextPainter.setPlaceholderDimensions], the method that usually takes
/// the layout results from this method as the input. /// the layout results from this method as the input.
@protected @protected
List<PlaceholderDimensions> layoutInlineChildren(double maxWidth, ChildLayouter layoutChild) { List<PlaceholderDimensions> layoutInlineChildren(double maxWidth, ChildLayouter layoutChild, ChildBaselineGetter getChildBaseline) {
final BoxConstraints constraints = BoxConstraints(maxWidth: maxWidth);
return <PlaceholderDimensions>[ return <PlaceholderDimensions>[
for (RenderBox? child = firstChild; child != null; child = childAfter(child)) for (RenderBox? child = firstChild; child != null; child = childAfter(child))
_layoutChild(child, maxWidth, layoutChild), _layoutChild(child, constraints, layoutChild, getChildBaseline),
]; ];
} }
@ -352,7 +357,6 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
case RenderComparison.paint: case RenderComparison.paint:
_textPainter.text = value; _textPainter.text = value;
_cachedAttributedLabels = null; _cachedAttributedLabels = null;
_canComputeIntrinsicsCached = null;
_cachedCombinedSemanticsInfos = null; _cachedCombinedSemanticsInfos = null;
markNeedsPaint(); markNeedsPaint();
markNeedsSemanticsUpdate(); markNeedsSemanticsUpdate();
@ -361,7 +365,6 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
_overflowShader = null; _overflowShader = null;
_cachedAttributedLabels = null; _cachedAttributedLabels = null;
_cachedCombinedSemanticsInfos = null; _cachedCombinedSemanticsInfos = null;
_canComputeIntrinsicsCached = null;
markNeedsLayout(); markNeedsLayout();
_removeSelectionRegistrarSubscription(); _removeSelectionRegistrarSubscription();
_disposeSelectableFragments(); _disposeSelectableFragments();
@ -671,12 +674,10 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
@override @override
double computeMinIntrinsicWidth(double height) { double computeMinIntrinsicWidth(double height) {
if (!_canComputeIntrinsics()) {
return 0.0;
}
final List<PlaceholderDimensions> placeholderDimensions = layoutInlineChildren( final List<PlaceholderDimensions> placeholderDimensions = layoutInlineChildren(
double.infinity, double.infinity,
(RenderBox child, BoxConstraints constraints) => Size(child.getMinIntrinsicWidth(double.infinity), 0.0), (RenderBox child, BoxConstraints constraints) => Size(child.getMinIntrinsicWidth(double.infinity), 0.0),
ChildLayoutHelper.getDryBaseline,
); );
return (_textIntrinsics..setPlaceholderDimensions(placeholderDimensions)..layout()) return (_textIntrinsics..setPlaceholderDimensions(placeholderDimensions)..layout())
.minIntrinsicWidth; .minIntrinsicWidth;
@ -684,25 +685,20 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) {
if (!_canComputeIntrinsics()) {
return 0.0;
}
final List<PlaceholderDimensions> placeholderDimensions = layoutInlineChildren( final List<PlaceholderDimensions> placeholderDimensions = layoutInlineChildren(
double.infinity, double.infinity,
// Height and baseline is irrelevant as all text will be laid // Height and baseline is irrelevant as all text will be laid
// out in a single line. Therefore, using 0.0 as a dummy for the height. // out in a single line. Therefore, using 0.0 as a dummy for the height.
(RenderBox child, BoxConstraints constraints) => Size(child.getMaxIntrinsicWidth(double.infinity), 0.0), (RenderBox child, BoxConstraints constraints) => Size(child.getMaxIntrinsicWidth(double.infinity), 0.0),
ChildLayoutHelper.getDryBaseline,
); );
return (_textIntrinsics..setPlaceholderDimensions(placeholderDimensions)..layout()) return (_textIntrinsics..setPlaceholderDimensions(placeholderDimensions)..layout())
.maxIntrinsicWidth; .maxIntrinsicWidth;
} }
double _computeIntrinsicHeight(double width) { double _computeIntrinsicHeight(double width) {
if (!_canComputeIntrinsics()) {
return 0.0;
}
return (_textIntrinsics return (_textIntrinsics
..setPlaceholderDimensions(layoutInlineChildren(width, ChildLayoutHelper.dryLayoutChild)) ..setPlaceholderDimensions(layoutInlineChildren(width, ChildLayoutHelper.dryLayoutChild, ChildLayoutHelper.getDryBaseline))
..layout(minWidth: width, maxWidth: _adjustMaxWidth(width))) ..layout(minWidth: width, maxWidth: _adjustMaxWidth(width)))
.height; .height;
} }
@ -717,52 +713,6 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
return _computeIntrinsicHeight(width); return _computeIntrinsicHeight(width);
} }
@override
double computeDistanceToActualBaseline(TextBaseline baseline) {
assert(!debugNeedsLayout);
assert(constraints.debugAssertIsValid());
_layoutTextWithConstraints(constraints);
// TODO(garyq): Since our metric for ideographic baseline is currently
// inaccurate and the non-alphabetic baselines are based off of the
// alphabetic baseline, we use the alphabetic for now to produce correct
// layouts. We should eventually change this back to pass the `baseline`
// property when the ideographic baseline is properly implemented
// (https://github.com/flutter/flutter/issues/22625).
return _textPainter.computeDistanceToActualBaseline(TextBaseline.alphabetic);
}
/// Whether all inline widget children of this [RenderBox] support dry layout
/// calculation.
bool _canComputeDryLayoutForInlineWidgets() {
// Dry layout cannot be calculated without a full layout for
// alignments that require the baseline (baseline, aboveBaseline,
// belowBaseline).
return text.visitChildren((InlineSpan span) {
return (span is! PlaceholderSpan) || switch (span.alignment) {
ui.PlaceholderAlignment.baseline ||
ui.PlaceholderAlignment.aboveBaseline ||
ui.PlaceholderAlignment.belowBaseline => false,
ui.PlaceholderAlignment.top ||
ui.PlaceholderAlignment.middle ||
ui.PlaceholderAlignment.bottom => true,
};
});
}
bool? _canComputeIntrinsicsCached;
// Intrinsics cannot be calculated without a full layout for
// alignments that require the baseline (baseline, aboveBaseline,
// belowBaseline).
bool _canComputeIntrinsics() {
final bool returnValue = _canComputeIntrinsicsCached ??= _canComputeDryLayoutForInlineWidgets();
assert(
returnValue || RenderObject.debugCheckingIntrinsics,
'Intrinsics are not available for PlaceholderAlignment.baseline, '
'PlaceholderAlignment.aboveBaseline, or PlaceholderAlignment.belowBaseline.',
);
return returnValue;
}
@override @override
bool hitTestSelf(Offset position) => true; bool hitTestSelf(Offset position) => true;
@ -822,23 +772,40 @@ class RenderParagraph extends RenderBox with ContainerRenderObjectMixin<RenderBo
@override @override
@protected @protected
Size computeDryLayout(covariant BoxConstraints constraints) { Size computeDryLayout(covariant BoxConstraints constraints) {
if (!_canComputeIntrinsics()) {
assert(debugCannotComputeDryLayout(
reason: 'Dry layout not available for alignments that require baseline.',
));
return Size.zero;
}
final Size size = (_textIntrinsics final Size size = (_textIntrinsics
..setPlaceholderDimensions(layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.dryLayoutChild)) ..setPlaceholderDimensions(layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.dryLayoutChild, ChildLayoutHelper.getDryBaseline))
..layout(minWidth: constraints.minWidth, maxWidth: _adjustMaxWidth(constraints.maxWidth))) ..layout(minWidth: constraints.minWidth, maxWidth: _adjustMaxWidth(constraints.maxWidth)))
.size; .size;
return constraints.constrain(size); return constraints.constrain(size);
} }
@override
double computeDistanceToActualBaseline(TextBaseline baseline) {
assert(!debugNeedsLayout);
assert(constraints.debugAssertIsValid());
_layoutTextWithConstraints(constraints);
// TODO(garyq): Since our metric for ideographic baseline is currently
// inaccurate and the non-alphabetic baselines are based off of the
// alphabetic baseline, we use the alphabetic for now to produce correct
// layouts. We should eventually change this back to pass the `baseline`
// property when the ideographic baseline is properly implemented
// (https://github.com/flutter/flutter/issues/22625).
return _textPainter.computeDistanceToActualBaseline(TextBaseline.alphabetic);
}
@override
double computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
assert(constraints.debugAssertIsValid());
_textIntrinsics
..setPlaceholderDimensions(layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.dryLayoutChild, ChildLayoutHelper.getDryBaseline))
..layout(minWidth: constraints.minWidth, maxWidth: _adjustMaxWidth(constraints.maxWidth));
return _textIntrinsics.computeDistanceToActualBaseline(TextBaseline.alphabetic);
}
@override @override
void performLayout() { void performLayout() {
final BoxConstraints constraints = this.constraints; final BoxConstraints constraints = this.constraints;
_placeholderDimensions = layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.layoutChild); _placeholderDimensions = layoutInlineChildren(constraints.maxWidth, ChildLayoutHelper.layoutChild, ChildLayoutHelper.getBaseline);
_layoutTextWithConstraints(constraints); _layoutTextWithConstraints(constraints);
positionInlineChildren(_textPainter.inlinePlaceholderBoxes!); positionInlineChildren(_textPainter.inlinePlaceholderBoxes!);

View File

@ -94,6 +94,12 @@ mixin RenderProxyBoxMixin<T extends RenderBox> on RenderBox, RenderObjectWithChi
?? super.computeDistanceToActualBaseline(baseline); ?? super.computeDistanceToActualBaseline(baseline);
} }
@override
@protected
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
return child?.getDryBaseline(constraints, baseline);
}
@override @override
@protected @protected
Size computeDryLayout(covariant BoxConstraints constraints) { Size computeDryLayout(covariant BoxConstraints constraints) {
@ -292,11 +298,8 @@ class RenderConstrainedBox extends RenderProxyBox {
@override @override
@protected @protected
Size computeDryLayout(covariant BoxConstraints constraints) { Size computeDryLayout(covariant BoxConstraints constraints) {
if (child != null) { return child?.getDryLayout(_additionalConstraints.enforce(constraints))
return child!.getDryLayout(_additionalConstraints.enforce(constraints)); ?? _additionalConstraints.enforce(constraints).constrain(Size.zero);
} else {
return _additionalConstraints.enforce(constraints).constrain(Size.zero);
}
} }
@override @override
@ -566,6 +569,11 @@ class RenderAspectRatio extends RenderProxyBox {
return _applyAspectRatio(constraints); return _applyAspectRatio(constraints);
} }
@override
double? computeDryBaseline(BoxConstraints constraints, TextBaseline baseline) {
return super.computeDryBaseline(BoxConstraints.tight(getDryLayout(constraints)), baseline);
}
@override @override
void performLayout() { void performLayout() {
size = getDryLayout(constraints); size = getDryLayout(constraints);
@ -702,22 +710,16 @@ class RenderIntrinsicWidth extends RenderProxyBox {
return _applyStep(height, _stepHeight); return _applyStep(height, _stepHeight);
} }
BoxConstraints _childConstraints(RenderBox child, BoxConstraints constraints) {
return constraints.tighten(
width: constraints.hasTightWidth ? null : _applyStep(child.getMaxIntrinsicWidth(constraints.maxHeight), _stepWidth),
height: stepHeight == null ? null : _applyStep(child.getMaxIntrinsicHeight(constraints.maxWidth), _stepHeight),
);
}
Size _computeSize({required ChildLayouter layoutChild, required BoxConstraints constraints}) { Size _computeSize({required ChildLayouter layoutChild, required BoxConstraints constraints}) {
if (child != null) { final RenderBox? child = this.child;
if (!constraints.hasTightWidth) { return child == null ? constraints.smallest : layoutChild(child, _childConstraints(child, constraints));
final double width = child!.getMaxIntrinsicWidth(constraints.maxHeight);
assert(width.isFinite);
constraints = constraints.tighten(width: _applyStep(width, _stepWidth));
}
if (_stepHeight != null) {
final double height = child!.getMaxIntrinsicHeight(constraints.maxWidth);
assert(height.isFinite);
constraints = constraints.tighten(height: _applyStep(height, _stepHeight));
}
return layoutChild(child!, constraints);
} else {
return constraints.smallest;
}
} }
@override @override
@ -729,6 +731,12 @@ class RenderIntrinsicWidth extends RenderProxyBox {
); );
} }
@override
double? computeDryBaseline(BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
return child?.getDryBaseline(_childConstraints(child, constraints), baseline);
}
@override @override
void performLayout() { void performLayout() {
size = _computeSize( size = _computeSize(
@ -808,17 +816,15 @@ class RenderIntrinsicHeight extends RenderProxyBox {
return getMaxIntrinsicHeight(width); return getMaxIntrinsicHeight(width);
} }
BoxConstraints _childConstraints(RenderBox child, BoxConstraints constraints) {
return constraints.hasTightHeight
? constraints
: constraints.tighten(height: child.getMaxIntrinsicHeight(constraints.maxWidth));
}
Size _computeSize({required ChildLayouter layoutChild, required BoxConstraints constraints}) { Size _computeSize({required ChildLayouter layoutChild, required BoxConstraints constraints}) {
if (child != null) { final RenderBox? child = this.child;
if (!constraints.hasTightHeight) { return child == null ? constraints.smallest : layoutChild(child, _childConstraints(child, constraints));
final double height = child!.getMaxIntrinsicHeight(constraints.maxWidth);
assert(height.isFinite);
constraints = constraints.tighten(height: height);
}
return layoutChild(child!, constraints);
} else {
return constraints.smallest;
}
} }
@override @override
@ -830,6 +836,12 @@ class RenderIntrinsicHeight extends RenderProxyBox {
); );
} }
@override
double? computeDryBaseline(BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
return child?.getDryBaseline(_childConstraints(child, constraints), baseline);
}
@override @override
void performLayout() { void performLayout() {
size = _computeSize( size = _computeSize(
@ -2596,15 +2608,9 @@ class RenderFittedBox extends RenderProxyBox {
_clipBehavior = clipBehavior, _clipBehavior = clipBehavior,
super(child); super(child);
Alignment _resolve() => _resolvedAlignment ??= alignment.resolve(textDirection);
Alignment? _resolvedAlignment; Alignment? _resolvedAlignment;
void _resolve() {
if (_resolvedAlignment != null) {
return;
}
_resolvedAlignment = alignment.resolve(textDirection);
}
void _markNeedResolution() { void _markNeedResolution() {
_resolvedAlignment = null; _resolvedAlignment = null;
markNeedsPaint(); markNeedsPaint();
@ -2772,13 +2778,13 @@ class RenderFittedBox extends RenderProxyBox {
_hasVisualOverflow = false; _hasVisualOverflow = false;
_transform = Matrix4.identity(); _transform = Matrix4.identity();
} else { } else {
_resolve(); final Alignment resolvedAlignment = _resolve();
final Size childSize = child!.size; final Size childSize = child!.size;
final FittedSizes sizes = applyBoxFit(_fit, childSize, size); final FittedSizes sizes = applyBoxFit(_fit, childSize, size);
final double scaleX = sizes.destination.width / sizes.source.width; final double scaleX = sizes.destination.width / sizes.source.width;
final double scaleY = sizes.destination.height / sizes.source.height; final double scaleY = sizes.destination.height / sizes.source.height;
final Rect sourceRect = _resolvedAlignment!.inscribe(sizes.source, Offset.zero & childSize); final Rect sourceRect = resolvedAlignment.inscribe(sizes.source, Offset.zero & childSize);
final Rect destinationRect = _resolvedAlignment!.inscribe(sizes.destination, Offset.zero & size); final Rect destinationRect = resolvedAlignment.inscribe(sizes.destination, Offset.zero & size);
_hasVisualOverflow = sourceRect.width < childSize.width || sourceRect.height < childSize.height; _hasVisualOverflow = sourceRect.width < childSize.width || sourceRect.height < childSize.height;
assert(scaleX.isFinite && scaleY.isFinite); assert(scaleX.isFinite && scaleY.isFinite);
_transform = Matrix4.translationValues(destinationRect.left, destinationRect.top, 0.0) _transform = Matrix4.translationValues(destinationRect.left, destinationRect.top, 0.0)
@ -3699,6 +3705,11 @@ class RenderOffstage extends RenderProxyBox {
@override @override
bool get sizedByParent => offstage; bool get sizedByParent => offstage;
@override
double? computeDryBaseline(BoxConstraints constraints, TextBaseline baseline) {
return offstage ? null : super.computeDryBaseline(constraints, baseline);
}
@override @override
@protected @protected
Size computeDryLayout(covariant BoxConstraints constraints) { Size computeDryLayout(covariant BoxConstraints constraints) {

View File

@ -114,18 +114,15 @@ class RenderPadding extends RenderShiftedBox {
_padding = padding, _padding = padding,
super(child); super(child);
EdgeInsets? _resolvedPadding; EdgeInsets? _resolvedPaddingCache;
EdgeInsets get _resolvedPadding {
void _resolve() { final EdgeInsets returnValue = _resolvedPaddingCache ??= padding.resolve(textDirection);
if (_resolvedPadding != null) { assert(returnValue.isNonNegative);
return; return returnValue;
}
_resolvedPadding = padding.resolve(textDirection);
assert(_resolvedPadding!.isNonNegative);
} }
void _markNeedResolution() { void _markNeedResolution() {
_resolvedPadding = null; _resolvedPaddingCache = null;
markNeedsLayout(); markNeedsLayout();
} }
@ -160,90 +157,86 @@ class RenderPadding extends RenderShiftedBox {
@override @override
double computeMinIntrinsicWidth(double height) { double computeMinIntrinsicWidth(double height) {
_resolve(); final EdgeInsets padding = _resolvedPadding;
final double totalHorizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
final double totalVerticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
if (child != null) { if (child != null) {
// Relies on double.infinity absorption. // Relies on double.infinity absorption.
return child!.getMinIntrinsicWidth(math.max(0.0, height - totalVerticalPadding)) + totalHorizontalPadding; return child!.getMinIntrinsicWidth(math.max(0.0, height - padding.vertical)) + padding.horizontal;
} }
return totalHorizontalPadding; return padding.horizontal;
} }
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) {
_resolve(); final EdgeInsets padding = _resolvedPadding;
final double totalHorizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
final double totalVerticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
if (child != null) { if (child != null) {
// Relies on double.infinity absorption. // Relies on double.infinity absorption.
return child!.getMaxIntrinsicWidth(math.max(0.0, height - totalVerticalPadding)) + totalHorizontalPadding; return child!.getMaxIntrinsicWidth(math.max(0.0, height - padding.vertical)) + padding.horizontal;
} }
return totalHorizontalPadding; return padding.horizontal;
} }
@override @override
double computeMinIntrinsicHeight(double width) { double computeMinIntrinsicHeight(double width) {
_resolve(); final EdgeInsets padding = _resolvedPadding;
final double totalHorizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
final double totalVerticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
if (child != null) { if (child != null) {
// Relies on double.infinity absorption. // Relies on double.infinity absorption.
return child!.getMinIntrinsicHeight(math.max(0.0, width - totalHorizontalPadding)) + totalVerticalPadding; return child!.getMinIntrinsicHeight(math.max(0.0, width - padding.horizontal)) + padding.vertical;
} }
return totalVerticalPadding; return padding.vertical;
} }
@override @override
double computeMaxIntrinsicHeight(double width) { double computeMaxIntrinsicHeight(double width) {
_resolve(); final EdgeInsets padding = _resolvedPadding;
final double totalHorizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
final double totalVerticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
if (child != null) { if (child != null) {
// Relies on double.infinity absorption. // Relies on double.infinity absorption.
return child!.getMaxIntrinsicHeight(math.max(0.0, width - totalHorizontalPadding)) + totalVerticalPadding; return child!.getMaxIntrinsicHeight(math.max(0.0, width - padding.horizontal)) + padding.vertical;
} }
return totalVerticalPadding; return padding.vertical;
} }
@override @override
@protected @protected
Size computeDryLayout(covariant BoxConstraints constraints) { Size computeDryLayout(covariant BoxConstraints constraints) {
_resolve(); final EdgeInsets padding = _resolvedPadding;
assert(_resolvedPadding != null);
if (child == null) { if (child == null) {
return constraints.constrain(Size( return constraints.constrain(Size(padding.horizontal, padding.vertical));
_resolvedPadding!.left + _resolvedPadding!.right,
_resolvedPadding!.top + _resolvedPadding!.bottom,
));
} }
final BoxConstraints innerConstraints = constraints.deflate(_resolvedPadding!); final BoxConstraints innerConstraints = constraints.deflate(padding);
final Size childSize = child!.getDryLayout(innerConstraints); final Size childSize = child!.getDryLayout(innerConstraints);
return constraints.constrain(Size( return constraints.constrain(Size(
_resolvedPadding!.left + childSize.width + _resolvedPadding!.right, padding.horizontal + childSize.width,
_resolvedPadding!.top + childSize.height + _resolvedPadding!.bottom, padding.vertical + childSize.height,
)); ));
} }
@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
if (child == null) {
return null;
}
final EdgeInsets padding = _resolvedPadding;
final BoxConstraints innerConstraints = constraints.deflate(padding);
final BaselineOffset result = BaselineOffset(child.getDryBaseline(innerConstraints, baseline)) + padding.top;
return result.offset;
}
@override @override
void performLayout() { void performLayout() {
final BoxConstraints constraints = this.constraints; final BoxConstraints constraints = this.constraints;
_resolve(); final EdgeInsets padding = _resolvedPadding;
assert(_resolvedPadding != null);
if (child == null) { if (child == null) {
size = constraints.constrain(Size( size = constraints.constrain(Size(padding.horizontal, padding.vertical));
_resolvedPadding!.left + _resolvedPadding!.right,
_resolvedPadding!.top + _resolvedPadding!.bottom,
));
return; return;
} }
final BoxConstraints innerConstraints = constraints.deflate(_resolvedPadding!); final BoxConstraints innerConstraints = constraints.deflate(padding);
child!.layout(innerConstraints, parentUsesSize: true); child!.layout(innerConstraints, parentUsesSize: true);
final BoxParentData childParentData = child!.parentData! as BoxParentData; final BoxParentData childParentData = child!.parentData! as BoxParentData;
childParentData.offset = Offset(_resolvedPadding!.left, _resolvedPadding!.top); childParentData.offset = Offset(padding.left, padding.top);
size = constraints.constrain(Size( size = constraints.constrain(Size(
_resolvedPadding!.left + child!.size.width + _resolvedPadding!.right, padding.horizontal + child!.size.width,
_resolvedPadding!.top + child!.size.height + _resolvedPadding!.bottom, padding.vertical + child!.size.height,
)); ));
} }
@ -252,7 +245,7 @@ class RenderPadding extends RenderShiftedBox {
super.debugPaintSize(context, offset); super.debugPaintSize(context, offset);
assert(() { assert(() {
final Rect outerRect = offset & size; final Rect outerRect = offset & size;
debugPaintPadding(context.canvas, outerRect, child != null ? _resolvedPadding!.deflateRect(outerRect) : null); debugPaintPadding(context.canvas, outerRect, child != null ? _resolvedPaddingCache!.deflateRect(outerRect) : null);
return true; return true;
}()); }());
} }
@ -690,6 +683,22 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
}; };
} }
@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
if (child == null) {
return null;
}
final BoxConstraints childConstraints = _getInnerConstraints(constraints);
final double? result = child.getDryBaseline(childConstraints, baseline);
if (result == null) {
return null;
}
final Size childSize = child.getDryLayout(childConstraints);
final Size size = getDryLayout(constraints);
return result + resolvedAlignment.alongOffset(size - childSize as Offset).dy;
}
@override @override
void performLayout() { void performLayout() {
if (child != null) { if (child != null) {
@ -841,6 +850,22 @@ class RenderConstraintsTransformBox extends RenderAligningShiftedBox with DebugO
return childSize == null ? constraints.smallest : constraints.constrain(childSize); return childSize == null ? constraints.smallest : constraints.constrain(childSize);
} }
@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
if (child == null) {
return null;
}
final BoxConstraints childConstraints = constraintsTransform(constraints);
final double? result = child.getDryBaseline(childConstraints, baseline);
if (result == null) {
return null;
}
final Size childSize = child.getDryLayout(childConstraints);
final Size size = constraints.constrain(childSize);
return result + resolvedAlignment.alongOffset(size - childSize as Offset).dy;
}
Rect _overflowContainerRect = Rect.zero; Rect _overflowContainerRect = Rect.zero;
Rect _overflowChildRect = Rect.zero; Rect _overflowChildRect = Rect.zero;
bool _isOverflowing = false; bool _isOverflowing = false;
@ -997,10 +1022,23 @@ class RenderSizedOverflowBox extends RenderAligningShiftedBox {
@override @override
double? computeDistanceToActualBaseline(TextBaseline baseline) { double? computeDistanceToActualBaseline(TextBaseline baseline) {
if (child != null) { return child?.getDistanceToActualBaseline(baseline)
return child!.getDistanceToActualBaseline(baseline); ?? super.computeDistanceToActualBaseline(baseline);
}
@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
if (child == null) {
return null;
} }
return super.computeDistanceToActualBaseline(baseline); final double? result = child.getDryBaseline(constraints, baseline);
if (result == null) {
return null;
}
final Size childSize = child.getDryLayout(constraints);
final Size size = getDryLayout(constraints);
return result + resolvedAlignment.alongOffset(size - childSize as Offset).dy;
} }
@override @override
@ -1163,6 +1201,22 @@ class RenderFractionallySizedOverflowBox extends RenderAligningShiftedBox {
return constraints.constrain(_getInnerConstraints(constraints).constrain(Size.zero)); return constraints.constrain(_getInnerConstraints(constraints).constrain(Size.zero));
} }
@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
if (child == null) {
return null;
}
final BoxConstraints childConstraints = _getInnerConstraints(constraints);
final double? result = child.getDryBaseline(childConstraints, baseline);
if (result == null) {
return null;
}
final Size childSize = child.getDryLayout(childConstraints);
final Size size = getDryLayout(constraints);
return result + resolvedAlignment.alongOffset(size - childSize as Offset).dy;
}
@override @override
void performLayout() { void performLayout() {
if (child != null) { if (child != null) {
@ -1359,6 +1413,23 @@ class RenderCustomSingleChildLayoutBox extends RenderShiftedBox {
return _getSize(constraints); return _getSize(constraints);
} }
@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
final RenderBox? child = this.child;
if (child == null) {
return null;
}
final BoxConstraints childConstraints = delegate.getConstraintsForChild(constraints);
final double? result = child.getDryBaseline(childConstraints, baseline);
if (result == null) {
return null;
}
return result + delegate.getPositionForChild(
_getSize(constraints),
childConstraints.isTight ? childConstraints.smallest : child.getDryLayout(childConstraints),
).dy;
}
@override @override
void performLayout() { void performLayout() {
size = _getSize(constraints); size = _getSize(constraints);

View File

@ -325,6 +325,15 @@ class _RenderLayoutBuilder extends RenderBox with RenderObjectWithChildMixin<Ren
return Size.zero; return Size.zero;
} }
@override
double? computeDryBaseline(BoxConstraints constraints, TextBaseline baseline) {
assert(debugCannotComputeDryLayout(reason:
'Calculating the dry baseline would require running the layout callback '
'speculatively, which might mutate the live render object tree.',
));
return null;
}
@override @override
void performLayout() { void performLayout() {
final BoxConstraints constraints = this.constraints; final BoxConstraints constraints = this.constraints;
@ -339,10 +348,8 @@ class _RenderLayoutBuilder extends RenderBox with RenderObjectWithChildMixin<Ren
@override @override
double? computeDistanceToActualBaseline(TextBaseline baseline) { double? computeDistanceToActualBaseline(TextBaseline baseline) {
if (child != null) { return child?.getDistanceToActualBaseline(baseline)
return child!.getDistanceToActualBaseline(baseline); ?? super.computeDistanceToActualBaseline(baseline);
}
return super.computeDistanceToActualBaseline(baseline);
} }
@override @override

View File

@ -410,6 +410,44 @@ class _RenderOverflowBar extends RenderBox
return defaultComputeDistanceToHighestActualBaseline(baseline); return defaultComputeDistanceToHighestActualBaseline(baseline);
} }
@override
double? computeDryBaseline(BoxConstraints constraints, TextBaseline baseline) {
final BoxConstraints childConstraints = constraints.loosen();
final (RenderBox? Function(RenderBox) next, RenderBox? startChild) = switch (overflowDirection) {
VerticalDirection.down => (childAfter, firstChild),
VerticalDirection.up => (childBefore, lastChild),
};
double maxChildHeight = 0.0;
double y = 0.0;
double childrenWidth = 0.0;
BaselineOffset minHorizontalBaseline = BaselineOffset.noBaseline;
BaselineOffset verticalBaseline = BaselineOffset.noBaseline;
for (RenderBox? child = startChild; child != null; child = next(child)) {
final Size childSize = child.getDryLayout(childConstraints);
final double heightDiff = childSize.height - maxChildHeight;
if (heightDiff > 0) {
minHorizontalBaseline += heightDiff / 2;
maxChildHeight = childSize.height;
}
final BaselineOffset baselineOffset = BaselineOffset(child.getDryBaseline(childConstraints, baseline));
if (baselineOffset != null) {
verticalBaseline ??= baselineOffset + y;
minHorizontalBaseline = minHorizontalBaseline.minOf(baselineOffset + (maxChildHeight - childSize.height));
}
y += childSize.height + overflowSpacing;
childrenWidth += childSize.width;
}
assert((verticalBaseline == null) == (minHorizontalBaseline == null));
return childrenWidth + spacing * (childCount - 1) > constraints.maxWidth
? verticalBaseline.offset
: minHorizontalBaseline.offset;
}
@override @override
Size computeDryLayout(BoxConstraints constraints) { Size computeDryLayout(BoxConstraints constraints) {
RenderBox? child = firstChild; RenderBox? child = firstChild;

View File

@ -379,6 +379,12 @@ class _RenderScaledInlineWidget extends RenderBox with RenderObjectWithChildMixi
}; };
} }
@override
double? computeDryBaseline(BoxConstraints constraints, TextBaseline baseline) {
final double? distance = child?.getDryBaseline(BoxConstraints(maxWidth: constraints.maxWidth / scale), baseline);
return distance == null ? null : scale * distance;
}
@override @override
Size computeDryLayout(BoxConstraints constraints) { Size computeDryLayout(BoxConstraints constraints) {
assert(!constraints.hasBoundedHeight); assert(!constraints.hasBoundedHeight);

View File

@ -21,16 +21,18 @@ class TestSingleChildLayoutDelegate extends SingleChildLayoutDelegate {
@override @override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) { BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
assert(!RenderObject.debugCheckingIntrinsics); if (!RenderObject.debugCheckingIntrinsics) {
constraintsFromGetConstraintsForChild = constraints; constraintsFromGetConstraintsForChild = constraints;
}
return const BoxConstraints(minWidth: 100.0, maxWidth: 150.0, minHeight: 200.0, maxHeight: 400.0); return const BoxConstraints(minWidth: 100.0, maxWidth: 150.0, minHeight: 200.0, maxHeight: 400.0);
} }
@override @override
Offset getPositionForChild(Size size, Size childSize) { Offset getPositionForChild(Size size, Size childSize) {
assert(!RenderObject.debugCheckingIntrinsics); if (!RenderObject.debugCheckingIntrinsics) {
sizeFromGetPositionForChild = size; sizeFromGetPositionForChild = size;
childSizeFromGetPositionForChild = childSize; childSizeFromGetPositionForChild = childSize;
}
return Offset.zero; return Offset.zero;
} }