Skip to content

Commit 97b172b

Browse files
daohoangsonclaude
andauthored
fix: implement computeDryBaseline for custom RenderBox subclasses (#1584)
In Flutter 3.32+, any RenderBox that overrides computeDistanceToActualBaseline must also override computeDryBaseline. Add computeDryBaseline to: - _ListItemRenderObject - _ListMarkerRenderObject - _RubyRenderObject - _TableRenderObject - _RenderCssSizing (pass correct child constraints) - _HorizontalMarginRenderObject (pass deflated constraints) - _ValignBaselineRenderObject (pass deflated constraints + padding) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 18c4b10 commit 97b172b

13 files changed

Lines changed: 314 additions & 0 deletions

packages/core/lib/src/widgets/css_sizing.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,20 @@ class _RenderCssSizing extends RenderProxyBox {
219219
markNeedsLayout();
220220
}
221221

222+
@override
223+
double? computeDryBaseline(
224+
BoxConstraints constraints,
225+
TextBaseline baseline,
226+
) {
227+
final scopedChild = child;
228+
if (scopedChild == null) {
229+
return null;
230+
}
231+
232+
final cc = _applyContraints(constraints);
233+
return scopedChild.getDryBaseline(cc, baseline);
234+
}
235+
222236
@override
223237
Size computeDryLayout(BoxConstraints constraints) {
224238
final scopedChild = child;

packages/core/lib/src/widgets/horizontal_margin.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ class _HorizontalMarginRenderObject extends RenderShiftedBox {
5555

5656
double get marginsOrZero => _left.or(0) + _right.or(0);
5757

58+
@override
59+
double? computeDryBaseline(
60+
BoxConstraints constraints,
61+
TextBaseline baseline,
62+
) {
63+
final scopedChild = child;
64+
if (scopedChild == null) {
65+
return null;
66+
}
67+
68+
final edges = EdgeInsets.only(left: _left.or(0), right: _right.or(0));
69+
final cc = constraints.deflate(edges);
70+
return scopedChild.getDryBaseline(cc, baseline);
71+
}
72+
5873
@override
5974
Size computeDryLayout(BoxConstraints constraints) =>
6075
_compute(child, constraints, ChildLayoutHelper.dryLayoutChild);

packages/core/lib/src/widgets/html_list_item.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ class _ListItemRenderObject extends RenderBox
8787
double? computeDistanceToActualBaseline(TextBaseline baseline) =>
8888
defaultComputeDistanceToFirstActualBaseline(baseline);
8989

90+
@override
91+
double? computeDryBaseline(
92+
BoxConstraints constraints,
93+
TextBaseline baseline,
94+
) {
95+
final child = firstChild;
96+
if (child == null) {
97+
return null;
98+
}
99+
100+
final childConstraints = constraints.maxWidth.isFinite && textAlign != null
101+
? constraints.tighten(width: constraints.maxWidth)
102+
: constraints;
103+
// The first child's offset is always Offset.zero in _compute
104+
return child.getDryBaseline(childConstraints, baseline);
105+
}
106+
90107
@override
91108
Size computeDryLayout(BoxConstraints constraints) =>
92109
_compute(firstChild, constraints, ChildLayoutHelper.dryLayoutChild);

packages/core/lib/src/widgets/html_list_marker.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ class _ListMarkerRenderObject extends RenderBox {
9797
double computeDistanceToActualBaseline(TextBaseline baseline) =>
9898
_textPainter.computeDistanceToActualBaseline(baseline);
9999

100+
@override
101+
double? computeDryBaseline(
102+
BoxConstraints constraints,
103+
TextBaseline baseline,
104+
) =>
105+
_textPainter.computeDistanceToActualBaseline(baseline);
106+
100107
@override
101108
Size computeDryLayout(BoxConstraints constraints) =>
102109
constraints.constrain(_textPainter.size);

packages/core/lib/src/widgets/html_ruby.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,37 @@ class _RubyRenderObject extends RenderBox
3939
return offset.dy + rubyValue;
4040
}
4141

42+
@override
43+
double? computeDryBaseline(
44+
BoxConstraints constraints,
45+
TextBaseline baseline,
46+
) {
47+
final ruby = firstChild;
48+
if (ruby == null) {
49+
return null;
50+
}
51+
52+
final rubyConstraints = constraints.loosen();
53+
final rubySize = ruby.getDryLayout(rubyConstraints);
54+
final rubyBaseline = ruby.getDryBaseline(rubyConstraints, baseline);
55+
if (rubyBaseline == null) {
56+
return null;
57+
}
58+
59+
final rt = (ruby.parentData! as _RubyParentData).nextSibling;
60+
final rtHeight = rt != null
61+
? rt
62+
.getDryLayout(
63+
rubyConstraints.copyWith(
64+
maxHeight: rubyConstraints.maxHeight - rubySize.height,
65+
),
66+
)
67+
.height
68+
: 0.0;
69+
70+
return rtHeight + rubyBaseline;
71+
}
72+
4273
@override
4374
Size computeDryLayout(BoxConstraints constraints) =>
4475
_compute(firstChild, constraints, ChildLayoutHelper.dryLayoutChild);

packages/core/lib/src/widgets/html_table.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,40 @@ class _TableRenderObject extends RenderBox
836836
return result;
837837
}
838838

839+
@override
840+
double? computeDryBaseline(
841+
BoxConstraints constraints,
842+
TextBaseline baseline,
843+
) {
844+
double? result;
845+
846+
var child = firstChild;
847+
while (child != null) {
848+
final data = child.parentData! as _TableCellData;
849+
850+
if (data.rowStart == 0) {
851+
// only compute cells in the first row
852+
// use the child's dry baseline with loosened constraints
853+
// since exact cell constraints require full table layout
854+
final candidate = child.getDryBaseline(constraints.loosen(), baseline);
855+
if (candidate != null) {
856+
final adjusted = candidate + paddingTop + rowGap;
857+
if (result != null) {
858+
if (adjusted < result) {
859+
result = adjusted;
860+
}
861+
} else {
862+
result = adjusted;
863+
}
864+
}
865+
}
866+
867+
child = data.nextSibling;
868+
}
869+
870+
return result;
871+
}
872+
839873
@override
840874
Size computeDryLayout(BoxConstraints constraints) =>
841875
_TableRenderLayouter.dry(this, constraints).compute(firstChild).totalSize;

packages/core/lib/src/widgets/valign_baseline.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,25 @@ class _ValignBaselineRenderObject extends RenderProxyBox {
121121
double? _baselineWithOffset;
122122
var _paddingTop = 0.0;
123123

124+
@override
125+
double? computeDryBaseline(
126+
BoxConstraints constraints,
127+
TextBaseline baseline,
128+
) {
129+
final scopedChild = child;
130+
if (scopedChild == null) {
131+
return null;
132+
}
133+
134+
final cc = constraints.loosen().deflate(EdgeInsets.only(top: _paddingTop));
135+
final childBaseline = scopedChild.getDryBaseline(cc, baseline);
136+
if (childBaseline == null) {
137+
return null;
138+
}
139+
140+
return childBaseline + _paddingTop;
141+
}
142+
124143
@override
125144
Size computeDryLayout(BoxConstraints constraints) =>
126145
_compute(child, constraints, ChildLayoutHelper.dryLayoutChild);

packages/core/test/style_margin_test.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,33 @@ void main() {
799799
expect(drySize, equals(const Size(50, 10)));
800800
});
801801

802+
testWidgets('computeDryBaseline', (tester) async {
803+
final key = GlobalKey();
804+
await tester.pumpWidget(
805+
MaterialApp(
806+
home: Scaffold(
807+
body: SizedBox(
808+
width: 100,
809+
child: HorizontalMargin(
810+
key: key,
811+
left: 10,
812+
right: 10,
813+
child: const Text('Hello'),
814+
),
815+
),
816+
),
817+
),
818+
);
819+
await tester.pumpAndSettle();
820+
821+
final renderBox = key.renderBox;
822+
final baseline = renderBox.getDryBaseline(
823+
renderBox.constraints,
824+
TextBaseline.alphabetic,
825+
);
826+
expect(baseline, isNotNull);
827+
});
828+
802829
group('computeMaxIntrinsicWidth', () {
803830
testWidgets('computes with child', (tester) async {
804831
await tester.pumpSizedBox(left: 1, right: 2);

packages/core/test/style_sizing_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,28 @@ Future<void> main() async {
591591
);
592592
});
593593

594+
testWidgets('computeDryBaseline', (tester) async {
595+
final key = GlobalKey();
596+
await tester.pumpWidget(
597+
MaterialApp(
598+
home: Scaffold(
599+
body: CssSizing(
600+
key: key,
601+
child: const Text('Hello'),
602+
),
603+
),
604+
),
605+
);
606+
await tester.pumpAndSettle();
607+
608+
final renderBox = key.renderBox;
609+
final baseline = renderBox.getDryBaseline(
610+
renderBox.constraints,
611+
TextBaseline.alphabetic,
612+
);
613+
expect(baseline, isNotNull);
614+
});
615+
594616
group('_guessChildSize with 2 dimensions', () {
595617
testWidgets('respect wide child', (tester) async {
596618
final key = GlobalKey();

packages/core/test/style_vertical_align_test.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import 'package:flutter/material.dart';
12
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
24
import 'package:mocktail_image_network/mocktail_image_network.dart';
35

46
import '_.dart';
@@ -278,4 +280,29 @@ void main() {
278280
expect(explained, contains('Foo'));
279281
});
280282
});
283+
284+
testWidgets('ValignBaseline computeDryBaseline', (tester) async {
285+
final key = GlobalKey();
286+
await tester.pumpWidget(
287+
MaterialApp(
288+
home: Scaffold(
289+
body: ValignBaselineContainer(
290+
child: ValignBaseline(
291+
key: key,
292+
index: 0,
293+
child: const Text('Hello'),
294+
),
295+
),
296+
),
297+
),
298+
);
299+
await tester.pumpAndSettle();
300+
301+
final renderBox = key.renderBox;
302+
final baseline = renderBox.getDryBaseline(
303+
renderBox.constraints,
304+
TextBaseline.alphabetic,
305+
);
306+
expect(baseline, isNotNull);
307+
});
281308
}

0 commit comments

Comments
 (0)