Skip to content

Commit 48f08e3

Browse files
authored
IgnoreBaseline widget (#131220)
Fixes flutter/flutter#7037
1 parent 2240649 commit 48f08e3

5 files changed

Lines changed: 142 additions & 3 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
916916
while (child != null) {
917917
assert(() {
918918
if (textBaseline == null) {
919-
throw FlutterError('To use FlexAlignItems.baseline, you must also specify which baseline to use using the "baseline" argument.');
919+
throw FlutterError('To use CrossAxisAlignment.baseline, you must also specify which baseline to use using the "textBaseline" argument.');
920920
}
921921
return true;
922922
}());

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,19 @@ class RenderIntrinsicHeight extends RenderProxyBox {
842842
}
843843
}
844844

845+
/// Excludes the child from baseline computations in the parent.
846+
class RenderIgnoreBaseline extends RenderProxyBox {
847+
/// Create a render object that causes the parent to ignore the child for baseline computations.
848+
RenderIgnoreBaseline({
849+
RenderBox? child,
850+
}) : super(child);
851+
852+
@override
853+
double? computeDistanceToActualBaseline(TextBaseline baseline) {
854+
return null;
855+
}
856+
}
857+
845858
/// Makes its child partially transparent.
846859
///
847860
/// This class paints its child into an intermediate buffer and then blends the

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,6 +1266,7 @@ class PhysicalShape extends SingleChildRenderObjectWidget {
12661266
}
12671267
}
12681268

1269+
12691270
// POSITIONING AND SIZING NODES
12701271

12711272
/// A widget that applies a transformation before painting its child.
@@ -3548,8 +3549,6 @@ class IntrinsicHeight extends SingleChildRenderObjectWidget {
35483549
/// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/).
35493550
class Baseline extends SingleChildRenderObjectWidget {
35503551
/// Creates a widget that positions its child according to the child's baseline.
3551-
///
3552-
/// The [baseline] and [baselineType] arguments must not be null.
35533552
const Baseline({
35543553
super.key,
35553554
required this.baseline,
@@ -3577,6 +3576,25 @@ class Baseline extends SingleChildRenderObjectWidget {
35773576
}
35783577
}
35793578

3579+
/// A widget that causes the parent to ignore the [child] for the purposes
3580+
/// of baseline alignment.
3581+
///
3582+
/// See also:
3583+
///
3584+
/// * [Baseline], a widget that positions a child relative to a baseline.
3585+
class IgnoreBaseline extends SingleChildRenderObjectWidget {
3586+
/// Creates a widget that ignores the child for baseline alignment purposes.
3587+
const IgnoreBaseline({
3588+
super.key,
3589+
super.child,
3590+
});
3591+
3592+
@override
3593+
RenderIgnoreBaseline createRenderObject(BuildContext context) {
3594+
return RenderIgnoreBaseline();
3595+
}
3596+
}
3597+
35803598

35813599
// SLIVERS
35823600

packages/flutter/test/rendering/baseline_test.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,56 @@ void main() {
5252
expect(childParentData.offset.dy, equals(10.0));
5353
expect(parent.size, equals(const Size(100.0, 110.0)));
5454
});
55+
56+
test('RenderFlex and RenderIgnoreBaseline (control test -- with baseline)', () {
57+
final RenderBox a, b;
58+
final RenderBox root = RenderFlex(
59+
crossAxisAlignment: CrossAxisAlignment.baseline,
60+
textBaseline: TextBaseline.alphabetic,
61+
textDirection: TextDirection.ltr,
62+
children: <RenderBox>[
63+
a = RenderParagraph(
64+
const TextSpan(text: 'a', style: TextStyle(fontSize: 128.0, fontFamily: 'FlutterTest')), // places baseline at y=96
65+
textDirection: TextDirection.ltr,
66+
),
67+
b = RenderParagraph(
68+
const TextSpan(text: 'b', style: TextStyle(fontSize: 32.0, fontFamily: 'FlutterTest')), // 24 above baseline, 8 below baseline
69+
textDirection: TextDirection.ltr,
70+
),
71+
],
72+
);
73+
layout(root);
74+
75+
final Offset aPos = a.localToGlobal(Offset.zero);
76+
final Offset bPos = b.localToGlobal(Offset.zero);
77+
expect(aPos.dy, 0.0);
78+
expect(bPos.dy, 96.0 - 24.0);
79+
});
80+
81+
test('RenderFlex and RenderIgnoreBaseline (with ignored baseline)', () {
82+
final RenderBox a, b;
83+
final RenderBox root = RenderFlex(
84+
crossAxisAlignment: CrossAxisAlignment.baseline,
85+
textBaseline: TextBaseline.alphabetic,
86+
textDirection: TextDirection.ltr,
87+
children: <RenderBox>[
88+
RenderIgnoreBaseline(
89+
child: a = RenderParagraph(
90+
const TextSpan(text: 'a', style: TextStyle(fontSize: 128.0, fontFamily: 'FlutterTest')),
91+
textDirection: TextDirection.ltr,
92+
),
93+
),
94+
b = RenderParagraph(
95+
const TextSpan(text: 'b', style: TextStyle(fontSize: 32.0, fontFamily: 'FlutterTest')),
96+
textDirection: TextDirection.ltr,
97+
),
98+
],
99+
);
100+
layout(root);
101+
102+
final Offset aPos = a.localToGlobal(Offset.zero);
103+
final Offset bPos = b.localToGlobal(Offset.zero);
104+
expect(aPos.dy, 0.0);
105+
expect(bPos.dy, 0.0);
106+
});
55107
}

packages/flutter/test/widgets/basic_test.dart

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,62 @@ void main() {
11351135
contains('verticalDirection: up'),
11361136
]));
11371137
});
1138+
1139+
testWidgets('Row and IgnoreBaseline (control -- with baseline)', (WidgetTester tester) async {
1140+
await tester.pumpWidget(
1141+
const Row(
1142+
crossAxisAlignment: CrossAxisAlignment.baseline,
1143+
textBaseline: TextBaseline.alphabetic,
1144+
textDirection: TextDirection.ltr,
1145+
children: <Widget>[
1146+
Text(
1147+
'a',
1148+
textDirection: TextDirection.ltr,
1149+
style: TextStyle(fontSize: 128.0, fontFamily: 'FlutterTest'), // places baseline at y=96
1150+
),
1151+
Text(
1152+
'b',
1153+
textDirection: TextDirection.ltr,
1154+
style: TextStyle(fontSize: 32.0, fontFamily: 'FlutterTest'), // 24 above baseline, 8 below baseline
1155+
),
1156+
],
1157+
),
1158+
);
1159+
1160+
final Offset aPos = tester.getTopLeft(find.text('a'));
1161+
final Offset bPos = tester.getTopLeft(find.text('b'));
1162+
expect(aPos.dy, 0.0);
1163+
expect(bPos.dy, 96.0 - 24.0);
1164+
});
1165+
1166+
testWidgets('Row and IgnoreBaseline (with ignored baseline)', (WidgetTester tester) async {
1167+
await tester.pumpWidget(
1168+
const Row(
1169+
crossAxisAlignment: CrossAxisAlignment.baseline,
1170+
textBaseline: TextBaseline.alphabetic,
1171+
textDirection: TextDirection.ltr,
1172+
children: <Widget>[
1173+
IgnoreBaseline(
1174+
child: Text(
1175+
'a',
1176+
textDirection: TextDirection.ltr,
1177+
style: TextStyle(fontSize: 128.0, fontFamily: 'FlutterTest'), // places baseline at y=96
1178+
),
1179+
),
1180+
Text(
1181+
'b',
1182+
textDirection: TextDirection.ltr,
1183+
style: TextStyle(fontSize: 32.0, fontFamily: 'FlutterTest'), // 24 above baseline, 8 below baseline
1184+
),
1185+
],
1186+
),
1187+
);
1188+
1189+
final Offset aPos = tester.getTopLeft(find.text('a'));
1190+
final Offset bPos = tester.getTopLeft(find.text('b'));
1191+
expect(aPos.dy, 0.0);
1192+
expect(bPos.dy, 0.0);
1193+
});
11381194
}
11391195

11401196
HitsRenderBox hits(RenderBox renderBox) => HitsRenderBox(renderBox);

0 commit comments

Comments
 (0)