Add new ListTile parameters to ListTileTheme (#69982)
This commit is contained in:
@@ -53,6 +53,9 @@ class ListTileTheme extends InheritedTheme {
|
||||
this.tileColor,
|
||||
this.selectedTileColor,
|
||||
this.enableFeedback,
|
||||
this.horizontalTitleGap,
|
||||
this.minVerticalPadding,
|
||||
this.minLeadingWidth,
|
||||
required Widget child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
@@ -72,6 +75,9 @@ class ListTileTheme extends InheritedTheme {
|
||||
Color? tileColor,
|
||||
Color? selectedTileColor,
|
||||
bool? enableFeedback,
|
||||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
required Widget child,
|
||||
}) {
|
||||
assert(child != null);
|
||||
@@ -90,6 +96,9 @@ class ListTileTheme extends InheritedTheme {
|
||||
tileColor: tileColor ?? parent.tileColor,
|
||||
selectedTileColor: selectedTileColor ?? parent.selectedTileColor,
|
||||
enableFeedback: enableFeedback ?? parent.enableFeedback,
|
||||
horizontalTitleGap: horizontalTitleGap ?? parent.horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding ?? parent.minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth ?? parent.minLeadingWidth,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
@@ -134,6 +143,21 @@ class ListTileTheme extends InheritedTheme {
|
||||
/// If [ListTile.selectedTileColor] is provided, [selectedTileColor] is ignored.
|
||||
final Color? selectedTileColor;
|
||||
|
||||
/// The horizontal gap between the titles and the leading/trailing widgets.
|
||||
///
|
||||
/// If specified, overrides the default value of [ListTile.horizontalTitleGap].
|
||||
final double? horizontalTitleGap;
|
||||
|
||||
/// The minimum padding on the top and bottom of the title and subtitle widgets.
|
||||
///
|
||||
/// If specified, overrides the default value of [ListTile.minVerticalPadding].
|
||||
final double? minVerticalPadding;
|
||||
|
||||
/// The minimum width allocated for the [ListTile.leading] widget.
|
||||
///
|
||||
/// If specified, overrides the default value of [ListTile.minLeadingWidth].
|
||||
final double? minLeadingWidth;
|
||||
|
||||
/// If specified, defines the feedback property for `ListTile`.
|
||||
///
|
||||
/// If [ListTile.enableFeedback] is provided, [enableFeedback] is ignored.
|
||||
@@ -164,6 +188,9 @@ class ListTileTheme extends InheritedTheme {
|
||||
tileColor: tileColor,
|
||||
selectedTileColor: selectedTileColor,
|
||||
enableFeedback: enableFeedback,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
@@ -179,7 +206,10 @@ class ListTileTheme extends InheritedTheme {
|
||||
|| contentPadding != oldWidget.contentPadding
|
||||
|| tileColor != oldWidget.tileColor
|
||||
|| selectedTileColor != oldWidget.selectedTileColor
|
||||
|| enableFeedback != oldWidget.enableFeedback;
|
||||
|| enableFeedback != oldWidget.enableFeedback
|
||||
|| horizontalTitleGap != oldWidget.horizontalTitleGap
|
||||
|| minVerticalPadding != oldWidget.minVerticalPadding
|
||||
|| minLeadingWidth != oldWidget.minLeadingWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,17 +749,14 @@ class ListTile extends StatelessWidget {
|
||||
this.tileColor,
|
||||
this.selectedTileColor,
|
||||
this.enableFeedback,
|
||||
this.horizontalTitleGap = 16.0,
|
||||
this.minVerticalPadding = 4.0,
|
||||
this.minLeadingWidth = 40.0,
|
||||
this.horizontalTitleGap,
|
||||
this.minVerticalPadding,
|
||||
this.minLeadingWidth,
|
||||
}) : assert(isThreeLine != null),
|
||||
assert(enabled != null),
|
||||
assert(selected != null),
|
||||
assert(autofocus != null),
|
||||
assert(!isThreeLine || subtitle != null),
|
||||
assert(horizontalTitleGap != null),
|
||||
assert(minVerticalPadding != null),
|
||||
assert(minLeadingWidth != null),
|
||||
super(key: key);
|
||||
|
||||
/// A widget to display before the title.
|
||||
@@ -929,13 +956,22 @@ class ListTile extends StatelessWidget {
|
||||
final bool? enableFeedback;
|
||||
|
||||
/// The horizontal gap between the titles and the leading/trailing widgets.
|
||||
final double horizontalTitleGap;
|
||||
///
|
||||
/// If null, then the value of [ListTileTheme.horizontalTitleGap] is used. If
|
||||
/// that is also null, then a default value of 16 is used.
|
||||
final double? horizontalTitleGap;
|
||||
|
||||
/// The minimum padding on the top and bottom of the title and subtitle widgets.
|
||||
final double minVerticalPadding;
|
||||
///
|
||||
/// If null, then the value of [ListTileTheme.minVerticalPadding] is used. If
|
||||
/// that is also null, then a default value of 4 is used.
|
||||
final double? minVerticalPadding;
|
||||
|
||||
/// The minimum leading width.
|
||||
final double minLeadingWidth;
|
||||
/// The minimum width allocated for the [ListTile.leading] widget.
|
||||
///
|
||||
/// If null, then the value of [ListTileTheme.minLeadingWidth] is used. If
|
||||
/// that is also null, then a default value of 40 is used.
|
||||
final double? minLeadingWidth;
|
||||
|
||||
/// Add a one pixel border in between each tile. If color isn't specified the
|
||||
/// [ThemeData.dividerColor] of the context's [Theme] is used.
|
||||
@@ -1104,12 +1140,11 @@ class ListTile extends StatelessWidget {
|
||||
|
||||
const EdgeInsets _defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0);
|
||||
final TextDirection textDirection = Directionality.of(context);
|
||||
final bool resolvedEnableFeedback = enableFeedback ?? tileTheme.enableFeedback ?? true;
|
||||
final EdgeInsets resolvedContentPadding = contentPadding?.resolve(textDirection)
|
||||
?? tileTheme.contentPadding?.resolve(textDirection)
|
||||
?? _defaultContentPadding;
|
||||
|
||||
final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs<MouseCursor>(
|
||||
final MouseCursor resolvedMouseCursor = MaterialStateProperty.resolveAs<MouseCursor>(
|
||||
mouseCursor ?? MaterialStateMouseCursor.clickable,
|
||||
<MaterialState>{
|
||||
if (!enabled || (onTap == null && onLongPress == null)) MaterialState.disabled,
|
||||
@@ -1121,13 +1156,13 @@ class ListTile extends StatelessWidget {
|
||||
customBorder: shape ?? tileTheme.shape,
|
||||
onTap: enabled ? onTap : null,
|
||||
onLongPress: enabled ? onLongPress : null,
|
||||
mouseCursor: effectiveMouseCursor,
|
||||
mouseCursor: resolvedMouseCursor,
|
||||
canRequestFocus: enabled,
|
||||
focusNode: focusNode,
|
||||
focusColor: focusColor,
|
||||
hoverColor: hoverColor,
|
||||
autofocus: autofocus,
|
||||
enableFeedback: resolvedEnableFeedback,
|
||||
enableFeedback: enableFeedback ?? tileTheme.enableFeedback ?? true,
|
||||
child: Semantics(
|
||||
selected: selected,
|
||||
enabled: enabled,
|
||||
@@ -1148,9 +1183,9 @@ class ListTile extends StatelessWidget {
|
||||
textDirection: textDirection,
|
||||
titleBaselineType: titleStyle.textBaseline!,
|
||||
subtitleBaselineType: subtitleStyle?.textBaseline,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth,
|
||||
horizontalTitleGap: horizontalTitleGap ?? tileTheme.horizontalTitleGap ?? 16,
|
||||
minVerticalPadding: minVerticalPadding ?? tileTheme.minVerticalPadding ?? 4,
|
||||
minLeadingWidth: minLeadingWidth ?? tileTheme.minLeadingWidth ?? 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1836,7 +1836,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('ListTile horizontalTitleGap = 0.0', (WidgetTester tester) async {
|
||||
Widget buildFrame(TextDirection textDirection) {
|
||||
Widget buildFrame(TextDirection textDirection, { double? themeHorizontalTitleGap, double? widgetHorizontalTitleGap }) {
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.zero,
|
||||
@@ -1845,13 +1845,16 @@ void main() {
|
||||
child: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Material(
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: const ListTile(
|
||||
horizontalTitleGap: 0.0,
|
||||
leading: Text('L'),
|
||||
title: Text('title'),
|
||||
trailing: Text('T'),
|
||||
child: ListTileTheme(
|
||||
horizontalTitleGap: themeHorizontalTitleGap,
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: ListTile(
|
||||
horizontalTitleGap: widgetHorizontalTitleGap,
|
||||
leading: const Text('L'),
|
||||
title: const Text('title'),
|
||||
trailing: const Text('T'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1862,15 +1865,29 @@ void main() {
|
||||
double left(String text) => tester.getTopLeft(find.text(text)).dx;
|
||||
double right(String text) => tester.getTopRight(find.text(text)).dx;
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr));
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, widgetHorizontalTitleGap: 0));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(left('title'), 56.0); // horizontalTitleGap: 0
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl));
|
||||
expect(left('title'), 56.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, themeHorizontalTitleGap: 0));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(right('title'), 744.0); // horizontalTitleGap: 0
|
||||
expect(left('title'), 56.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, themeHorizontalTitleGap: 10, widgetHorizontalTitleGap: 0));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(left('title'), 56.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, widgetHorizontalTitleGap: 0));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(right('title'), 744.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, themeHorizontalTitleGap: 0));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(right('title'), 744.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, themeHorizontalTitleGap: 10, widgetHorizontalTitleGap: 0));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(right('title'), 744.0);
|
||||
});
|
||||
|
||||
testWidgets('ListTile horizontalTitleGap = (default) && ListTile minLeadingWidth = (default)', (WidgetTester tester) async {
|
||||
@@ -1913,7 +1930,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('ListTile minVerticalPadding = 80.0', (WidgetTester tester) async {
|
||||
Widget buildFrame(TextDirection textDirection) {
|
||||
Widget buildFrame(TextDirection textDirection, { double? themeMinVerticalPadding, double? widgetMinVerticalPadding }) {
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.zero,
|
||||
@@ -1922,13 +1939,16 @@ void main() {
|
||||
child: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Material(
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: const ListTile(
|
||||
minVerticalPadding: 80.0,
|
||||
leading: Text('L'),
|
||||
title: Text('title'),
|
||||
trailing: Text('T'),
|
||||
child: ListTileTheme(
|
||||
minVerticalPadding: themeMinVerticalPadding,
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: ListTile(
|
||||
minVerticalPadding: widgetMinVerticalPadding,
|
||||
leading: const Text('L'),
|
||||
title: const Text('title'),
|
||||
trailing: const Text('T'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1937,21 +1957,29 @@ void main() {
|
||||
}
|
||||
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr));
|
||||
|
||||
// minVerticalPadding: 80.0
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, widgetMinVerticalPadding: 80));
|
||||
// 80 + 80 + 16(Title) = 176
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 176.0));
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl));
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, themeMinVerticalPadding: 80));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 176.0));
|
||||
|
||||
// minVerticalPadding: 80.0
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, themeMinVerticalPadding: 0, widgetMinVerticalPadding: 80));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 176.0));
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, widgetMinVerticalPadding: 80));
|
||||
// 80 + 80 + 16(Title) = 176
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 176.0));
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, themeMinVerticalPadding: 80));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 176.0));
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, themeMinVerticalPadding: 0, widgetMinVerticalPadding: 80));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 176.0));
|
||||
});
|
||||
|
||||
testWidgets('ListTile minLeadingWidth = 60.0', (WidgetTester tester) async {
|
||||
Widget buildFrame(TextDirection textDirection) {
|
||||
Widget buildFrame(TextDirection textDirection, { double? themeMinLeadingWidth, double? widgetMinLeadingWidth }) {
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.zero,
|
||||
@@ -1960,13 +1988,16 @@ void main() {
|
||||
child: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Material(
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: const ListTile(
|
||||
minLeadingWidth: 60.0,
|
||||
leading: Text('L'),
|
||||
title: Text('title'),
|
||||
trailing: Text('T'),
|
||||
child: ListTileTheme(
|
||||
minLeadingWidth: themeMinLeadingWidth,
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: ListTile(
|
||||
minLeadingWidth: widgetMinLeadingWidth,
|
||||
leading: const Text('L'),
|
||||
title: const Text('title'),
|
||||
trailing: const Text('T'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1977,18 +2008,31 @@ void main() {
|
||||
double left(String text) => tester.getTopLeft(find.text(text)).dx;
|
||||
double right(String text) => tester.getTopRight(find.text(text)).dx;
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr));
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, widgetMinLeadingWidth: 60));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
// minLeadingWidth: 60.0
|
||||
// 92.0 = 16.0(Default contentPadding) + 16.0(Default horizontalTitleGap) + 60.0
|
||||
expect(left('title'), 92.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl));
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, themeMinLeadingWidth: 60));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(left('title'), 92.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, themeMinLeadingWidth: 0, widgetMinLeadingWidth: 60));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(left('title'), 92.0);
|
||||
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, widgetMinLeadingWidth: 60));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
// minLeadingWidth: 60.0
|
||||
// 708.0 = 800.0 - (16.0(Default contentPadding) + 16.0(Default horizontalTitleGap) + 60.0)
|
||||
expect(right('title'), 708.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, themeMinLeadingWidth: 60));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(right('title'), 708.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl, themeMinLeadingWidth: 0, widgetMinLeadingWidth: 60));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(right('title'), 708.0);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user