mirror of
https://github.com/flutter/flutter
synced 2024-10-05 15:59:49 +00:00
Add a minTileHeight
to ListTile widget so its height can be customized to less than the default height. (#145244)
fixes: https://github.com/flutter/flutter/issues/145369
This commit is contained in:
parent
6190c5eea1
commit
2b317d584f
|
@ -251,6 +251,7 @@ class ExpansionTile extends StatefulWidget {
|
|||
this.controller,
|
||||
this.dense,
|
||||
this.visualDensity,
|
||||
this.minTileHeight,
|
||||
this.enableFeedback = true,
|
||||
this.enabled = true,
|
||||
this.expansionAnimationStyle,
|
||||
|
@ -508,6 +509,9 @@ class ExpansionTile extends StatefulWidget {
|
|||
/// {@macro flutter.material.themedata.visualDensity}
|
||||
final VisualDensity? visualDensity;
|
||||
|
||||
/// {@macro flutter.material.ListTile.minTileHeight}
|
||||
final double? minTileHeight;
|
||||
|
||||
/// {@macro flutter.material.ListTile.enableFeedback}
|
||||
final bool? enableFeedback;
|
||||
|
||||
|
@ -710,6 +714,7 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
|||
title: widget.title,
|
||||
subtitle: widget.subtitle,
|
||||
trailing: widget.trailing ?? _buildTrailingIcon(context),
|
||||
minTileHeight: widget.minTileHeight,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -364,6 +364,7 @@ class ListTile extends StatelessWidget {
|
|||
this.horizontalTitleGap,
|
||||
this.minVerticalPadding,
|
||||
this.minLeadingWidth,
|
||||
this.minTileHeight,
|
||||
this.titleAlignment,
|
||||
}) : assert(!isThreeLine || subtitle != null);
|
||||
|
||||
|
@ -669,6 +670,16 @@ class ListTile extends StatelessWidget {
|
|||
/// that is also null, then a default value of 40 is used.
|
||||
final double? minLeadingWidth;
|
||||
|
||||
/// {@template flutter.material.ListTile.minTileHeight}
|
||||
/// The minimum height allocated for the [ListTile] widget.
|
||||
///
|
||||
/// If this is null, default tile heights are 56.0, 72.0, and 88.0 for one,
|
||||
/// two, and three lines of text respectively. If `isDense` is true, these
|
||||
/// defaults are changed to 48.0, 64.0, and 76.0. A visual density value or
|
||||
/// a large title will also adjust the default tile heights.
|
||||
/// {@endtemplate}
|
||||
final double? minTileHeight;
|
||||
|
||||
/// Defines how [ListTile.leading] and [ListTile.trailing] are
|
||||
/// vertically aligned relative to the [ListTile]'s titles
|
||||
/// ([ListTile.title] and [ListTile.subtitle]).
|
||||
|
@ -884,6 +895,7 @@ class ListTile extends StatelessWidget {
|
|||
horizontalTitleGap: horizontalTitleGap ?? tileTheme.horizontalTitleGap ?? 16,
|
||||
minVerticalPadding: minVerticalPadding ?? tileTheme.minVerticalPadding ?? defaults.minVerticalPadding!,
|
||||
minLeadingWidth: minLeadingWidth ?? tileTheme.minLeadingWidth ?? defaults.minLeadingWidth!,
|
||||
minTileHeight: minTileHeight ?? tileTheme.minTileHeight,
|
||||
titleAlignment: effectiveTitleAlignment,
|
||||
),
|
||||
),
|
||||
|
@ -978,6 +990,7 @@ class _ListTile extends SlottedMultiChildRenderObjectWidget<_ListTileSlot, Rende
|
|||
required this.horizontalTitleGap,
|
||||
required this.minVerticalPadding,
|
||||
required this.minLeadingWidth,
|
||||
this.minTileHeight,
|
||||
this.subtitleBaselineType,
|
||||
required this.titleAlignment,
|
||||
});
|
||||
|
@ -995,6 +1008,7 @@ class _ListTile extends SlottedMultiChildRenderObjectWidget<_ListTileSlot, Rende
|
|||
final double horizontalTitleGap;
|
||||
final double minVerticalPadding;
|
||||
final double minLeadingWidth;
|
||||
final double? minTileHeight;
|
||||
final ListTileTitleAlignment titleAlignment;
|
||||
|
||||
@override
|
||||
|
@ -1022,6 +1036,7 @@ class _ListTile extends SlottedMultiChildRenderObjectWidget<_ListTileSlot, Rende
|
|||
horizontalTitleGap: horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth,
|
||||
minTileHeight: minTileHeight,
|
||||
titleAlignment: titleAlignment,
|
||||
);
|
||||
}
|
||||
|
@ -1037,6 +1052,7 @@ class _ListTile extends SlottedMultiChildRenderObjectWidget<_ListTileSlot, Rende
|
|||
..subtitleBaselineType = subtitleBaselineType
|
||||
..horizontalTitleGap = horizontalTitleGap
|
||||
..minLeadingWidth = minLeadingWidth
|
||||
..minTileHeight = minTileHeight
|
||||
..minVerticalPadding = minVerticalPadding
|
||||
..titleAlignment = titleAlignment;
|
||||
}
|
||||
|
@ -1053,7 +1069,8 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
|
|||
required double horizontalTitleGap,
|
||||
required double minVerticalPadding,
|
||||
required double minLeadingWidth,
|
||||
required ListTileTitleAlignment titleAlignment,
|
||||
double? minTileHeight,
|
||||
required ListTileTitleAlignment titleAlignment
|
||||
}) : _isDense = isDense,
|
||||
_visualDensity = visualDensity,
|
||||
_isThreeLine = isThreeLine,
|
||||
|
@ -1063,6 +1080,7 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
|
|||
_horizontalTitleGap = horizontalTitleGap,
|
||||
_minVerticalPadding = minVerticalPadding,
|
||||
_minLeadingWidth = minLeadingWidth,
|
||||
_minTileHeight = minTileHeight,
|
||||
_titleAlignment = titleAlignment;
|
||||
|
||||
RenderBox? get leading => childForSlot(_ListTileSlot.leading);
|
||||
|
@ -1179,6 +1197,16 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
|
|||
markNeedsLayout();
|
||||
}
|
||||
|
||||
double? _minTileHeight;
|
||||
double? get minTileHeight => _minTileHeight;
|
||||
set minTileHeight(double? value) {
|
||||
if (_minTileHeight == value) {
|
||||
return;
|
||||
}
|
||||
_minTileHeight = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
ListTileTitleAlignment get titleAlignment => _titleAlignment;
|
||||
ListTileTitleAlignment _titleAlignment;
|
||||
set titleAlignment(ListTileTitleAlignment value) {
|
||||
|
@ -1238,7 +1266,7 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
|
|||
@override
|
||||
double computeMinIntrinsicHeight(double width) {
|
||||
return math.max(
|
||||
_defaultTileHeight,
|
||||
minTileHeight ?? _defaultTileHeight,
|
||||
title!.getMinIntrinsicHeight(width) + (subtitle?.getMinIntrinsicHeight(width) ?? 0.0),
|
||||
);
|
||||
}
|
||||
|
@ -1345,19 +1373,17 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
|
|||
assert(isOneLine);
|
||||
}
|
||||
|
||||
final double defaultTileHeight = _defaultTileHeight;
|
||||
|
||||
double tileHeight;
|
||||
double titleY;
|
||||
double? subtitleY;
|
||||
if (!hasSubtitle) {
|
||||
tileHeight = math.max(defaultTileHeight, titleSize.height + 2.0 * _minVerticalPadding);
|
||||
tileHeight = math.max(minTileHeight ?? _defaultTileHeight, titleSize.height + 2.0 * _minVerticalPadding);
|
||||
titleY = (tileHeight - titleSize.height) / 2.0;
|
||||
} else {
|
||||
assert(subtitleBaselineType != null);
|
||||
titleY = titleBaseline! - _boxBaseline(title!, titleBaselineType)!;
|
||||
subtitleY = subtitleBaseline! - _boxBaseline(subtitle!, subtitleBaselineType!)! + visualDensity.vertical * 2.0;
|
||||
tileHeight = defaultTileHeight;
|
||||
tileHeight = minTileHeight ?? _defaultTileHeight;
|
||||
|
||||
// If the title and subtitle overlap, move the title upwards by half
|
||||
// the overlap and the subtitle down by the same amount, and adjust
|
||||
|
|
|
@ -63,6 +63,7 @@ class ListTileThemeData with Diagnosticable {
|
|||
this.enableFeedback,
|
||||
this.mouseCursor,
|
||||
this.visualDensity,
|
||||
this.minTileHeight,
|
||||
this.titleAlignment,
|
||||
});
|
||||
|
||||
|
@ -111,6 +112,9 @@ class ListTileThemeData with Diagnosticable {
|
|||
/// Overrides the default value of [ListTile.minLeadingWidth].
|
||||
final double? minLeadingWidth;
|
||||
|
||||
/// Overrides the default value of [ListTile.minTileHeight].
|
||||
final double? minTileHeight;
|
||||
|
||||
/// Overrides the default value of [ListTile.enableFeedback].
|
||||
final bool? enableFeedback;
|
||||
|
||||
|
@ -141,6 +145,7 @@ class ListTileThemeData with Diagnosticable {
|
|||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
double? minTileHeight,
|
||||
bool? enableFeedback,
|
||||
MaterialStateProperty<MouseCursor?>? mouseCursor,
|
||||
bool? isThreeLine,
|
||||
|
@ -163,6 +168,7 @@ class ListTileThemeData with Diagnosticable {
|
|||
horizontalTitleGap: horizontalTitleGap ?? this.horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding ?? this.minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth ?? this.minLeadingWidth,
|
||||
minTileHeight: minTileHeight ?? this.minTileHeight,
|
||||
enableFeedback: enableFeedback ?? this.enableFeedback,
|
||||
mouseCursor: mouseCursor ?? this.mouseCursor,
|
||||
visualDensity: visualDensity ?? this.visualDensity,
|
||||
|
@ -191,6 +197,7 @@ class ListTileThemeData with Diagnosticable {
|
|||
horizontalTitleGap: lerpDouble(a?.horizontalTitleGap, b?.horizontalTitleGap, t),
|
||||
minVerticalPadding: lerpDouble(a?.minVerticalPadding, b?.minVerticalPadding, t),
|
||||
minLeadingWidth: lerpDouble(a?.minLeadingWidth, b?.minLeadingWidth, t),
|
||||
minTileHeight: lerpDouble(a?.minTileHeight, b?.minTileHeight, t),
|
||||
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
|
||||
mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
|
||||
visualDensity: t < 0.5 ? a?.visualDensity : b?.visualDensity,
|
||||
|
@ -215,6 +222,7 @@ class ListTileThemeData with Diagnosticable {
|
|||
horizontalTitleGap,
|
||||
minVerticalPadding,
|
||||
minLeadingWidth,
|
||||
minTileHeight,
|
||||
enableFeedback,
|
||||
mouseCursor,
|
||||
visualDensity,
|
||||
|
@ -245,6 +253,7 @@ class ListTileThemeData with Diagnosticable {
|
|||
&& other.horizontalTitleGap == horizontalTitleGap
|
||||
&& other.minVerticalPadding == minVerticalPadding
|
||||
&& other.minLeadingWidth == minLeadingWidth
|
||||
&& other.minTileHeight == minTileHeight
|
||||
&& other.enableFeedback == enableFeedback
|
||||
&& other.mouseCursor == mouseCursor
|
||||
&& other.visualDensity == visualDensity
|
||||
|
@ -269,6 +278,7 @@ class ListTileThemeData with Diagnosticable {
|
|||
properties.add(DoubleProperty('horizontalTitleGap', horizontalTitleGap, defaultValue: null));
|
||||
properties.add(DoubleProperty('minVerticalPadding', minVerticalPadding, defaultValue: null));
|
||||
properties.add(DoubleProperty('minLeadingWidth', minLeadingWidth, defaultValue: null));
|
||||
properties.add(DoubleProperty('minTileHeight', minTileHeight, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<VisualDensity>('visualDensity', visualDensity, defaultValue: null));
|
||||
|
@ -488,6 +498,7 @@ class ListTileTheme extends InheritedTheme {
|
|||
double? horizontalTitleGap,
|
||||
double? minVerticalPadding,
|
||||
double? minLeadingWidth,
|
||||
double? minTileHeight,
|
||||
ListTileTitleAlignment? titleAlignment,
|
||||
MaterialStateProperty<MouseCursor?>? mouseCursor,
|
||||
VisualDensity? visualDensity,
|
||||
|
@ -515,6 +526,7 @@ class ListTileTheme extends InheritedTheme {
|
|||
horizontalTitleGap: horizontalTitleGap ?? parent.horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding ?? parent.minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth ?? parent.minLeadingWidth,
|
||||
minTileHeight: minTileHeight ?? parent.minTileHeight,
|
||||
titleAlignment: titleAlignment ?? parent.titleAlignment,
|
||||
mouseCursor: mouseCursor ?? parent.mouseCursor,
|
||||
visualDensity: visualDensity ?? parent.visualDensity,
|
||||
|
|
|
@ -1802,6 +1802,36 @@ void main() {
|
|||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(right('title'), 708.0);
|
||||
});
|
||||
testWidgets('ListTile minTileHeight', (WidgetTester tester) async {
|
||||
Widget buildFrame(TextDirection textDirection, { double? minTileHeight, }) {
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(),
|
||||
child: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Material(
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: ListTile(
|
||||
minTileHeight: minTileHeight,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default list tile with height = 56.0
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
|
||||
// Set list tile height = 30.0
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, minTileHeight: 30));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 30.0));
|
||||
|
||||
// Set list tile height = 60.0
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr, minTileHeight: 60));
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 60.0));
|
||||
});
|
||||
|
||||
testWidgets('colors are applied to leading and trailing text widgets', (WidgetTester tester) async {
|
||||
final Key leadingKey = UniqueKey();
|
||||
|
|
|
@ -72,6 +72,7 @@ void main() {
|
|||
expect(themeData.horizontalTitleGap, null);
|
||||
expect(themeData.minVerticalPadding, null);
|
||||
expect(themeData.minLeadingWidth, null);
|
||||
expect(themeData.minTileHeight, null);
|
||||
expect(themeData.enableFeedback, null);
|
||||
expect(themeData.mouseCursor, null);
|
||||
expect(themeData.visualDensity, null);
|
||||
|
@ -108,6 +109,7 @@ void main() {
|
|||
horizontalTitleGap: 200,
|
||||
minVerticalPadding: 300,
|
||||
minLeadingWidth: 400,
|
||||
minTileHeight: 30,
|
||||
enableFeedback: true,
|
||||
mouseCursor: MaterialStateMouseCursor.clickable,
|
||||
visualDensity: VisualDensity.comfortable,
|
||||
|
@ -137,6 +139,7 @@ void main() {
|
|||
'horizontalTitleGap: 200.0',
|
||||
'minVerticalPadding: 300.0',
|
||||
'minLeadingWidth: 400.0',
|
||||
'minTileHeight: 30.0',
|
||||
'enableFeedback: true',
|
||||
'mouseCursor: WidgetStateMouseCursor(clickable)',
|
||||
'visualDensity: VisualDensity#00000(h: -1.0, v: -1.0)(horizontal: -1.0, vertical: -1.0)',
|
||||
|
@ -916,6 +919,7 @@ void main() {
|
|||
horizontalTitleGap: 200,
|
||||
minVerticalPadding: 300,
|
||||
minLeadingWidth: 400,
|
||||
minTileHeight: 30,
|
||||
enableFeedback: true,
|
||||
titleAlignment: ListTileTitleAlignment.bottom,
|
||||
);
|
||||
|
@ -936,6 +940,7 @@ void main() {
|
|||
horizontalTitleGap: 600,
|
||||
minVerticalPadding: 700,
|
||||
minLeadingWidth: 800,
|
||||
minTileHeight: 80,
|
||||
enableFeedback: false,
|
||||
titleAlignment: ListTileTitleAlignment.top,
|
||||
);
|
||||
|
@ -955,6 +960,7 @@ void main() {
|
|||
expect(copy.horizontalTitleGap, 600);
|
||||
expect(copy.minVerticalPadding, 700);
|
||||
expect(copy.minLeadingWidth, 800);
|
||||
expect(copy.minTileHeight, 80);
|
||||
expect(copy.enableFeedback, false);
|
||||
expect(copy.titleAlignment, ListTileTitleAlignment.top);
|
||||
});
|
||||
|
@ -1015,6 +1021,7 @@ void main() {
|
|||
horizontalTitleGap: 200,
|
||||
minVerticalPadding: 300,
|
||||
minLeadingWidth: 400,
|
||||
minTileHeight: 30,
|
||||
enableFeedback: true,
|
||||
titleAlignment: ListTileTitleAlignment.bottom,
|
||||
mouseCursor: MaterialStateMouseCursor.textable,
|
||||
|
@ -1041,6 +1048,7 @@ void main() {
|
|||
horizontalTitleGap: 600,
|
||||
minVerticalPadding: 700,
|
||||
minLeadingWidth: 800,
|
||||
minTileHeight: 80,
|
||||
enableFeedback: false,
|
||||
titleAlignment: ListTileTitleAlignment.top,
|
||||
mouseCursor: MaterialStateMouseCursor.clickable,
|
||||
|
@ -1071,6 +1079,7 @@ void main() {
|
|||
expect(theme.horizontalTitleGap, 600);
|
||||
expect(theme.minVerticalPadding, 700);
|
||||
expect(theme.minLeadingWidth, 800);
|
||||
expect(theme.minTileHeight, 80);
|
||||
expect(theme.enableFeedback, false);
|
||||
expect(theme.titleAlignment, ListTileTitleAlignment.top);
|
||||
expect(theme.mouseCursor, MaterialStateMouseCursor.clickable);
|
||||
|
|
Loading…
Reference in a new issue