Skip to content

Commit 6e035e3

Browse files
Copilotdaohoangson
andauthored
Fix text-align handling for <li> content layout (#1578)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: daohoangson <239336+daohoangson@users.noreply.github.com>
1 parent d01a4b4 commit 6e035e3

3 files changed

Lines changed: 103 additions & 21 deletions

File tree

packages/core/lib/src/internal/ops/tag_li.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class TagLi {
107107

108108
return HtmlListItem(
109109
marker: marker,
110+
textAlign: resolved.get<TextAlign>(),
110111
textDirection: resolved.directionOrLtr,
111112
child: child,
112113
);

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

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ const _kGapVsMarker = 5.0;
55

66
/// A list item widget.
77
class HtmlListItem extends MultiChildRenderObjectWidget {
8+
/// The alignment of the item contents.
9+
final TextAlign? textAlign;
10+
811
/// The directionality of the item.
912
final TextDirection textDirection;
1013

@@ -13,6 +16,7 @@ class HtmlListItem extends MultiChildRenderObjectWidget {
1316
Widget? child,
1417
super.key,
1518
Widget? marker,
19+
this.textAlign,
1620
required this.textDirection,
1721
}) : super(
1822
children: child != null
@@ -25,17 +29,24 @@ class HtmlListItem extends MultiChildRenderObjectWidget {
2529

2630
@override
2731
RenderObject createRenderObject(BuildContext context) =>
28-
_ListItemRenderObject(textDirection: textDirection);
32+
_ListItemRenderObject(
33+
textAlign: textAlign,
34+
textDirection: textDirection,
35+
);
2936

3037
@override
3138
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
3239
super.debugFillProperties(properties);
40+
properties.add(EnumProperty('textAlign', textAlign, defaultValue: null));
3341
properties.add(DiagnosticsProperty('textDirection', textDirection));
3442
}
3543

3644
@override
37-
void updateRenderObject(BuildContext context, RenderObject renderObject) =>
38-
(renderObject as _ListItemRenderObject).textDirection = textDirection;
45+
void updateRenderObject(BuildContext context, RenderObject renderObject) {
46+
renderObject as _ListItemRenderObject
47+
..textAlign = textAlign
48+
..textDirection = textDirection;
49+
}
3950
}
4051

4152
class _ListItemData extends ContainerBoxParentData<RenderBox> {}
@@ -45,8 +56,21 @@ class _ListItemRenderObject extends RenderBox
4556
ContainerRenderObjectMixin<RenderBox, _ListItemData>,
4657
RenderBoxContainerDefaultsMixin<RenderBox, _ListItemData> {
4758
_ListItemRenderObject({
59+
TextAlign? textAlign,
4860
required TextDirection textDirection,
49-
}) : _textDirection = textDirection;
61+
}) : _textAlign = textAlign,
62+
_textDirection = textDirection;
63+
64+
TextAlign? get textAlign => _textAlign;
65+
TextAlign? _textAlign;
66+
set textAlign(TextAlign? value) {
67+
if (_textAlign == value) {
68+
return;
69+
}
70+
71+
_textAlign = value;
72+
markNeedsLayout();
73+
}
5074

5175
TextDirection get textDirection => _textDirection;
5276
TextDirection _textDirection;
@@ -112,28 +136,35 @@ class _ListItemRenderObject extends RenderBox
112136
}
113137

114138
final childData = child.parentData! as _ListItemData;
115-
final childSize = fn(child, bc);
139+
final childConstraints = bc.maxWidth.isFinite && textAlign != null
140+
? bc.tighten(width: bc.maxWidth)
141+
: bc;
142+
final childSize = fn(child, childConstraints);
116143
final marker = childData.nextSibling;
117144
final markerSize = marker != null ? fn(marker, bc.loosen()) : Size.zero;
118145
final height = childSize.height > 0 ? childSize.height : markerSize.height;
119146
final size = bc.constrain(Size(childSize.width, height));
120147

121-
if (identical(fn, ChildLayoutHelper.layoutChild) && marker != null) {
122-
const baseline = TextBaseline.alphabetic;
123-
final markerDistance =
124-
marker.getDistanceToBaseline(baseline, onlyReal: true) ??
125-
markerSize.height;
126-
final childDistance =
127-
child.getDistanceToBaseline(baseline, onlyReal: true) ??
128-
markerDistance;
129-
130-
final markerData = marker.parentData! as _ListItemData;
131-
markerData.offset = Offset(
132-
textDirection == TextDirection.ltr
133-
? -markerSize.width - _kGapVsMarker
134-
: childSize.width + _kGapVsMarker,
135-
childDistance - markerDistance,
136-
);
148+
if (identical(fn, ChildLayoutHelper.layoutChild)) {
149+
childData.offset = Offset.zero;
150+
151+
if (marker != null) {
152+
const baseline = TextBaseline.alphabetic;
153+
final markerDistance =
154+
marker.getDistanceToBaseline(baseline, onlyReal: true) ??
155+
markerSize.height;
156+
final childDistance =
157+
child.getDistanceToBaseline(baseline, onlyReal: true) ??
158+
markerDistance;
159+
160+
final markerData = marker.parentData! as _ListItemData;
161+
markerData.offset = Offset(
162+
textDirection == TextDirection.ltr
163+
? -markerSize.width - _kGapVsMarker
164+
: childSize.width + _kGapVsMarker,
165+
childDistance - markerDistance,
166+
);
167+
}
137168
}
138169

139170
return size;

packages/core/test/tag_li_test.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:io';
22

33
import 'package:flutter/material.dart';
4+
import 'package:flutter/rendering.dart';
45
import 'package:flutter_test/flutter_test.dart';
56
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
67
import 'package:golden_toolkit/golden_toolkit.dart';
@@ -71,6 +72,30 @@ Future<void> main() async {
7172
);
7273
});
7374

75+
testWidgets('renders centered text within list item', (tester) async {
76+
await tester.pumpWidget(
77+
const MaterialApp(
78+
home: Scaffold(
79+
body: SizedBox(
80+
width: 200,
81+
child: HtmlWidget(
82+
'<ol><li style="text-align: center">data 1</li></ol>',
83+
),
84+
),
85+
),
86+
),
87+
);
88+
await tester.pumpAndSettle();
89+
90+
final itemRect = tester.getRect(find.byType(HtmlListItem));
91+
final paragraph = tester.renderObject<RenderParagraph>(findText('data 1'));
92+
final textRect = tester.getRect(findText('data 1'));
93+
94+
expect(itemRect.width, greaterThan(100));
95+
expect(paragraph.textAlign, equals(TextAlign.center));
96+
expect(textRect.center.dx, closeTo(itemRect.center.dx, .1));
97+
});
98+
7499
testWidgets('renders nested list', (WidgetTester tester) async {
75100
const html = '''
76101
<ul>
@@ -876,6 +901,31 @@ Future<void> main() async {
876901
);
877902
});
878903

904+
testWidgets('computeDryLayout with textAlign', (tester) async {
905+
final key = GlobalKey();
906+
await tester.pumpWidget(
907+
MaterialApp(
908+
home: Scaffold(
909+
body: SizedBox(
910+
width: 100,
911+
child: HtmlListItem(
912+
key: key,
913+
textAlign: TextAlign.center,
914+
textDirection: TextDirection.ltr,
915+
child: const SizedBox(width: 50, height: 5),
916+
),
917+
),
918+
),
919+
),
920+
);
921+
await tester.pumpAndSettle();
922+
923+
expect(
924+
key.renderBox.getDryLayout(const BoxConstraints(maxWidth: 100)),
925+
equals(const Size(100, 5)),
926+
);
927+
});
928+
879929
testWidgets('computeDryLayout with no op marker', (tester) async {
880930
final key = GlobalKey();
881931
await tester.pumpWidget(

0 commit comments

Comments
 (0)