Skip to content

Commit

Permalink
Implement computeDryBaseline for RenderChip (#146224)
Browse files Browse the repository at this point in the history
Also, use `getDryLayout` for the first pass of the `label` layout.  

The computeDryBaseline stuff doesn't have a test because there're asserts making sure it returns the same result as `computeDistanceToActualBaseline`. I have tried adding `+1` to the return expression and `flutter test` fails.
  • Loading branch information
LongCatIsLooong authored May 8, 2024
1 parent 4abd735 commit e37b29d
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 27 deletions.
57 changes: 30 additions & 27 deletions packages/flutter/lib/src/material/chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1728,9 +1728,6 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
bool get isDrawingCheckmark => theme.showCheckmark && !checkmarkAnimation.isDismissed;
bool get deleteIconShowing => !deleteDrawerAnimation.isDismissed;

@override
bool get sizedByParent => false;

static Rect _boxRect(RenderBox box) => _boxParentData(box).offset & box.size;

static BoxParentData _boxParentData(RenderBox box) => box.parentData! as BoxParentData;
Expand Down Expand Up @@ -1775,29 +1772,22 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
return (BaselineOffset(label.getDistanceToActualBaseline(baseline)) + _boxParentData(label).offset.dy).offset;
}

Size _layoutLabel(BoxConstraints contentConstraints, double iconSizes, Size size, Size rawSize, [ChildLayouter layoutChild = ChildLayoutHelper.layoutChild]) {
BoxConstraints _labelConstraintsFrom(BoxConstraints contentConstraints, double iconWidth, double contentSize, Size rawLabelSize) {
// Now that we know the label height and the width of the icons, we can
// determine how much to shrink the width constraints for the "real" layout.
final double maxLabelWidth = contentConstraints.maxWidth.isFinite
? math.max(
0.0,
contentConstraints.maxWidth - iconSizes - theme.labelPadding.horizontal - theme.padding.horizontal,
)
: size.width;
final BoxConstraints labelConstraints = BoxConstraints(
minHeight: rawSize.height,
maxHeight: size.height,
maxWidth: maxLabelWidth,
final double freeSpace = contentConstraints.maxWidth - iconWidth - theme.labelPadding.horizontal - theme.padding.horizontal;
final double maxLabelWidth = math.max(0.0, freeSpace);
return BoxConstraints(
minHeight: rawLabelSize.height,
maxHeight: contentSize,
maxWidth: maxLabelWidth.isFinite ? maxLabelWidth : rawLabelSize.width,
);
final Size updatedSize = layoutChild(label, labelConstraints);
return theme.labelPadding.inflateSize(updatedSize);
}

Size _layoutAvatar(double contentSize, [ChildLayouter layoutChild = ChildLayoutHelper.layoutChild]) {
final double requestedSize = math.max(0.0, contentSize);
final BoxConstraints avatarConstraints = avatarBoxConstraints ?? BoxConstraints.tightFor(
width: requestedSize,
height: requestedSize,
width: contentSize,
height: contentSize,
);
final Size avatarBoxSize = layoutChild(avatar, avatarConstraints);
if (!theme.showCheckmark && !theme.showAvatar) {
Expand All @@ -1808,10 +1798,9 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
}

Size _layoutDeleteIcon(double contentSize, [ChildLayouter layoutChild = ChildLayoutHelper.layoutChild]) {
final double requestedSize = math.max(0.0, contentSize);
final BoxConstraints deleteIconConstraints = deleteIconBoxConstraints ?? BoxConstraints.tightFor(
width: requestedSize,
height: requestedSize,
width: contentSize,
height: contentSize,
);
final Size boxSize = layoutChild(deleteIcon, deleteIconConstraints);
if (!deleteIconShowing) {
Expand Down Expand Up @@ -1851,25 +1840,36 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
return _computeSizes(constraints, ChildLayoutHelper.dryLayoutChild).size;
}

@override
double? computeDryBaseline(BoxConstraints constraints, TextBaseline baseline) {
final _ChipSizes sizes = _computeSizes(constraints, ChildLayoutHelper.dryLayoutChild);
final BaselineOffset labelBaseline = BaselineOffset(label.getDryBaseline(sizes.labelConstraints, baseline))
+ (sizes.content - sizes.label.height + sizes.densityAdjustment.dy) / 2
+ theme.padding.top + theme.labelPadding.top;
return labelBaseline.offset;
}

_ChipSizes _computeSizes(BoxConstraints constraints, ChildLayouter layoutChild) {
final BoxConstraints contentConstraints = constraints.loosen();
// Find out the height of the label within the constraints.
final Offset densityAdjustment = Offset(0.0, theme.visualDensity.baseSizeAdjustment.dy / 2.0);
final Size rawLabelSize = layoutChild(label, contentConstraints);
final Size rawLabelSize = label.getDryLayout(contentConstraints);
final double contentSize = math.max(
_kChipHeight - theme.padding.vertical + theme.labelPadding.vertical,
rawLabelSize.height + theme.labelPadding.vertical,
);
assert(contentSize >= rawLabelSize.height);
final Size avatarSize = _layoutAvatar(contentSize, layoutChild);
final Size deleteIconSize = _layoutDeleteIcon(contentSize, layoutChild);
final Size labelSize = _layoutLabel(

final BoxConstraints labelConstraints = _labelConstraintsFrom(
contentConstraints,
avatarSize.width + deleteIconSize.width,
Size(rawLabelSize.width, contentSize),
contentSize,
rawLabelSize,
layoutChild,
);

final Size labelSize = theme.labelPadding.inflateSize(layoutChild(label, labelConstraints));
final Offset densityAdjustment = Offset(0.0, theme.visualDensity.baseSizeAdjustment.dy / 2.0);
// This is the overall size of the content: it doesn't include
// theme.padding, that is added in at the end.
final Size overallSize = Size(
Expand All @@ -1887,6 +1887,7 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
content: contentSize,
densityAdjustment: densityAdjustment,
avatar: avatarSize,
labelConstraints: labelConstraints,
label: labelSize,
deleteIcon: deleteIconSize,
);
Expand Down Expand Up @@ -2232,6 +2233,7 @@ class _ChipSizes {
required this.overall,
required this.content,
required this.avatar,
required this.labelConstraints,
required this.label,
required this.deleteIcon,
required this.densityAdjustment,
Expand All @@ -2240,6 +2242,7 @@ class _ChipSizes {
final Size overall;
final double content;
final Size avatar;
final BoxConstraints labelConstraints;
final Size label;
final Size deleteIcon;
final Offset densityAdjustment;
Expand Down
25 changes: 25 additions & 0 deletions packages/flutter/test/material/chip_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5781,6 +5781,18 @@ void main() {
// Test rendered icon color.
expect(getIconStyle(tester, deleteIcon)?.color, deleteIconColor);
});

testWidgets('Chip label only does layout once', (WidgetTester tester) async {
final RenderLayoutCount renderLayoutCount = RenderLayoutCount();
final Widget layoutCounter = Center(
key: GlobalKey(),
child: WidgetToRenderBoxAdapter(renderBox: renderLayoutCount),
);

await tester.pumpWidget(wrapForChip(child: RawChip(label: layoutCounter)));

expect(renderLayoutCount.layoutCount, 1);
});
}

class _MaterialStateOutlinedBorder extends StadiumBorder implements MaterialStateOutlinedBorder {
Expand All @@ -5800,3 +5812,16 @@ class _MaterialStateBorderSide extends MaterialStateBorderSide {
@override
BorderSide? resolve(Set<MaterialState> states) => resolver(states);
}

class RenderLayoutCount extends RenderBox {
int layoutCount = 0;

@override
Size computeDryLayout(covariant BoxConstraints constraints) => constraints.biggest;

@override
void performLayout() {
layoutCount += 1;
size = constraints.biggest;
}
}

0 comments on commit e37b29d

Please sign in to comment.