Skip to content

Commit 9c4e49e

Browse files
authored
fix(ListTileTheme): isThreeLine is missing. (flutter#165481)
fix: flutter#165453 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
1 parent 7ab8ce9 commit 9c4e49e

File tree

4 files changed

+377
-6
lines changed

4 files changed

+377
-6
lines changed

packages/flutter/lib/src/material/list_tile.dart

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ class ListTile extends StatelessWidget {
393393
this.title,
394394
this.subtitle,
395395
this.trailing,
396-
this.isThreeLine = false,
396+
this.isThreeLine,
397397
this.dense,
398398
this.visualDensity,
399399
this.shape,
@@ -425,7 +425,7 @@ class ListTile extends StatelessWidget {
425425
this.minTileHeight,
426426
this.titleAlignment,
427427
this.internalAddSemanticForOnTap = true,
428-
}) : assert(!isThreeLine || subtitle != null);
428+
}) : assert(isThreeLine != true || subtitle != null);
429429

430430
/// A widget to display before the title.
431431
///
@@ -482,7 +482,12 @@ class ListTile extends StatelessWidget {
482482
///
483483
/// When using a [Text] widget for [title] and [subtitle], you can enforce
484484
/// line limits using [Text.maxLines].
485-
final bool isThreeLine;
485+
///
486+
/// See also:
487+
///
488+
/// * [ListTileTheme.of], which returns the nearest [ListTileTheme]'s
489+
/// [ListTileThemeData].
490+
final bool? isThreeLine;
486491

487492
/// {@template flutter.material.ListTile.dense}
488493
/// Whether this list tile is part of a vertically dense list.
@@ -987,7 +992,11 @@ class ListTile extends StatelessWidget {
987992
trailing: trailingIcon,
988993
isDense: _isDenseLayout(theme, tileTheme),
989994
visualDensity: visualDensity ?? tileTheme.visualDensity ?? theme.visualDensity,
990-
isThreeLine: isThreeLine,
995+
isThreeLine:
996+
isThreeLine ??
997+
tileTheme.isThreeLine ??
998+
theme.listTileTheme.isThreeLine ??
999+
false,
9911000
textDirection: textDirection,
9921001
titleBaselineType:
9931002
titleStyle.textBaseline ?? defaults.titleTextStyle!.textBaseline!,
@@ -1021,7 +1030,6 @@ class ListTile extends StatelessWidget {
10211030
ifTrue: 'THREE_LINE',
10221031
ifFalse: 'TWO_LINE',
10231032
showName: true,
1024-
defaultValue: false,
10251033
),
10261034
);
10271035
properties.add(

packages/flutter/lib/src/material/list_tile_theme.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class ListTileThemeData with Diagnosticable {
7373
this.minTileHeight,
7474
this.titleAlignment,
7575
this.controlAffinity,
76+
this.isThreeLine,
7677
});
7778

7879
/// Overrides the default value of [ListTile.dense].
@@ -139,6 +140,9 @@ class ListTileThemeData with Diagnosticable {
139140
/// or [ExpansionTile.controlAffinity] or [SwitchListTile.controlAffinity] or [RadioListTile.controlAffinity].
140141
final ListTileControlAffinity? controlAffinity;
141142

143+
/// If specified, overrides the default value of [ListTile.isThreeLine].
144+
final bool? isThreeLine;
145+
142146
/// Creates a copy of this object with the given fields replaced with the
143147
/// new values.
144148
ListTileThemeData copyWith({
@@ -187,6 +191,7 @@ class ListTileThemeData with Diagnosticable {
187191
visualDensity: visualDensity ?? this.visualDensity,
188192
titleAlignment: titleAlignment ?? this.titleAlignment,
189193
controlAffinity: controlAffinity ?? this.controlAffinity,
194+
isThreeLine: isThreeLine ?? this.isThreeLine,
190195
);
191196
}
192197

@@ -221,6 +226,7 @@ class ListTileThemeData with Diagnosticable {
221226
visualDensity: t < 0.5 ? a?.visualDensity : b?.visualDensity,
222227
titleAlignment: t < 0.5 ? a?.titleAlignment : b?.titleAlignment,
223228
controlAffinity: t < 0.5 ? a?.controlAffinity : b?.controlAffinity,
229+
isThreeLine: t < 0.5 ? a?.isThreeLine : b?.isThreeLine,
224230
);
225231
}
226232

@@ -247,6 +253,7 @@ class ListTileThemeData with Diagnosticable {
247253
visualDensity,
248254
titleAlignment,
249255
controlAffinity,
256+
isThreeLine,
250257
]);
251258

252259
@override
@@ -278,7 +285,8 @@ class ListTileThemeData with Diagnosticable {
278285
other.mouseCursor == mouseCursor &&
279286
other.visualDensity == visualDensity &&
280287
other.titleAlignment == titleAlignment &&
281-
other.controlAffinity == controlAffinity;
288+
other.controlAffinity == controlAffinity &&
289+
other.isThreeLine == isThreeLine;
282290
}
283291

284292
@override
@@ -337,6 +345,7 @@ class ListTileThemeData with Diagnosticable {
337345
defaultValue: null,
338346
),
339347
);
348+
properties.add(DiagnosticsProperty<bool>('isThreeLine', isThreeLine, defaultValue: null));
340349
}
341350
}
342351

@@ -573,6 +582,7 @@ class ListTileTheme extends InheritedTheme {
573582
MaterialStateProperty<MouseCursor?>? mouseCursor,
574583
VisualDensity? visualDensity,
575584
ListTileControlAffinity? controlAffinity,
585+
bool? isThreeLine,
576586
required Widget child,
577587
}) {
578588
return Builder(
@@ -603,6 +613,7 @@ class ListTileTheme extends InheritedTheme {
603613
mouseCursor: mouseCursor ?? parent.mouseCursor,
604614
visualDensity: visualDensity ?? parent.visualDensity,
605615
controlAffinity: controlAffinity ?? parent.controlAffinity,
616+
isThreeLine: isThreeLine ?? parent.isThreeLine,
606617
),
607618
child: child,
608619
);
@@ -627,6 +638,7 @@ class ListTileTheme extends InheritedTheme {
627638
horizontalTitleGap: horizontalTitleGap,
628639
minVerticalPadding: minVerticalPadding,
629640
minLeadingWidth: minLeadingWidth,
641+
isThreeLine: _data?.isThreeLine,
630642
),
631643
child: child,
632644
);

packages/flutter/test/material/list_tile_test.dart

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4361,6 +4361,138 @@ void main() {
43614361
expect(trailingOffset.dy - tileOffset.dy, minVerticalPadding);
43624362
});
43634363
});
4364+
4365+
// Regression test for https://github.com/flutter/flutter/issues/165453
4366+
testWidgets('ListTile isThreeLine', (WidgetTester tester) async {
4367+
const double height = 300;
4368+
const double avatarTop = 130.0;
4369+
const double placeholderTop = 138.0;
4370+
4371+
Widget buildFrame({bool? themeDataIsThreeLine, bool? themeIsThreeLine, bool? isThreeLine}) {
4372+
return MaterialApp(
4373+
key: UniqueKey(),
4374+
theme:
4375+
themeDataIsThreeLine != null
4376+
? ThemeData(listTileTheme: ListTileThemeData(isThreeLine: themeDataIsThreeLine))
4377+
: null,
4378+
home: Material(
4379+
child: ListTileTheme(
4380+
data:
4381+
themeIsThreeLine != null ? ListTileThemeData(isThreeLine: themeIsThreeLine) : null,
4382+
child: ListView(
4383+
children: <Widget>[
4384+
ListTile(
4385+
isThreeLine: isThreeLine,
4386+
leading: const CircleAvatar(),
4387+
trailing: const SizedBox(height: 24.0, width: 24.0, child: Placeholder()),
4388+
title: const Text('A'),
4389+
subtitle: const Text('A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM'),
4390+
),
4391+
ListTile(
4392+
isThreeLine: isThreeLine,
4393+
leading: const CircleAvatar(),
4394+
trailing: const SizedBox(height: 24.0, width: 24.0, child: Placeholder()),
4395+
title: const Text('A'),
4396+
subtitle: const Text('A'),
4397+
),
4398+
],
4399+
),
4400+
),
4401+
),
4402+
);
4403+
}
4404+
4405+
void expectTwoLine() {
4406+
expect(
4407+
tester.getRect(find.byType(ListTile).at(0)),
4408+
const Rect.fromLTWH(0.0, 0.0, 800.0, height),
4409+
);
4410+
expect(
4411+
tester.getRect(find.byType(CircleAvatar).at(0)),
4412+
const Rect.fromLTWH(16.0, avatarTop, 40.0, 40.0),
4413+
);
4414+
expect(
4415+
tester.getRect(find.byType(Placeholder).at(0)),
4416+
const Rect.fromLTWH(800.0 - 24.0 - 24.0, placeholderTop, 24.0, 24.0),
4417+
);
4418+
expect(
4419+
tester.getRect(find.byType(ListTile).at(1)),
4420+
const Rect.fromLTWH(0.0, height, 800.0, 72.0),
4421+
);
4422+
expect(
4423+
tester.getRect(find.byType(CircleAvatar).at(1)),
4424+
const Rect.fromLTWH(16.0, height + 16.0, 40.0, 40.0),
4425+
);
4426+
expect(
4427+
tester.getRect(find.byType(Placeholder).at(1)),
4428+
const Rect.fromLTWH(800.0 - 24.0 - 24.0, height + 24.0, 24.0, 24.0),
4429+
);
4430+
}
4431+
4432+
void expectThreeLine() {
4433+
expect(
4434+
tester.getRect(find.byType(ListTile).at(0)),
4435+
const Rect.fromLTWH(0.0, 0.0, 800.0, height),
4436+
);
4437+
expect(
4438+
tester.getRect(find.byType(CircleAvatar).at(0)),
4439+
const Rect.fromLTWH(16.0, 8.0, 40.0, 40.0),
4440+
);
4441+
expect(
4442+
tester.getRect(find.byType(Placeholder).at(0)),
4443+
const Rect.fromLTWH(800.0 - 24.0 - 24.0, 8.0, 24.0, 24.0),
4444+
);
4445+
expect(
4446+
tester.getRect(find.byType(ListTile).at(1)),
4447+
const Rect.fromLTWH(0.0, height, 800.0, 88.0),
4448+
);
4449+
expect(
4450+
tester.getRect(find.byType(CircleAvatar).at(1)),
4451+
const Rect.fromLTWH(16.0, height + 8.0, 40.0, 40.0),
4452+
);
4453+
expect(
4454+
tester.getRect(find.byType(Placeholder).at(1)),
4455+
const Rect.fromLTWH(800.0 - 24.0 - 24.0, height + 8.0, 24.0, 24.0),
4456+
);
4457+
}
4458+
4459+
await tester.pumpWidget(buildFrame());
4460+
expectTwoLine();
4461+
4462+
await tester.pumpWidget(buildFrame(themeDataIsThreeLine: true));
4463+
expectThreeLine();
4464+
4465+
await tester.pumpWidget(buildFrame(themeDataIsThreeLine: false, themeIsThreeLine: true));
4466+
expectThreeLine();
4467+
4468+
await tester.pumpWidget(buildFrame(themeDataIsThreeLine: true, themeIsThreeLine: false));
4469+
expectTwoLine();
4470+
4471+
await tester.pumpWidget(buildFrame(isThreeLine: true));
4472+
expectThreeLine();
4473+
4474+
await tester.pumpWidget(buildFrame(themeIsThreeLine: true, isThreeLine: false));
4475+
expectTwoLine();
4476+
4477+
await tester.pumpWidget(buildFrame(themeDataIsThreeLine: true, isThreeLine: false));
4478+
expectTwoLine();
4479+
4480+
await tester.pumpWidget(
4481+
buildFrame(themeDataIsThreeLine: true, themeIsThreeLine: true, isThreeLine: false),
4482+
);
4483+
expectTwoLine();
4484+
4485+
await tester.pumpWidget(buildFrame(themeIsThreeLine: false, isThreeLine: true));
4486+
expectThreeLine();
4487+
4488+
await tester.pumpWidget(buildFrame(themeDataIsThreeLine: false, isThreeLine: true));
4489+
expectThreeLine();
4490+
4491+
await tester.pumpWidget(
4492+
buildFrame(themeDataIsThreeLine: false, themeIsThreeLine: false, isThreeLine: true),
4493+
);
4494+
expectThreeLine();
4495+
});
43644496
}
43654497

43664498
RenderParagraph _getTextRenderObject(WidgetTester tester, String text) {

0 commit comments

Comments
 (0)