forked from firka/flutter
Allow modification of ListTile's horizontalTitleGap, minVerticalPadding, minLeadingWidth (#64222)
Allow modification of ListTile's horizontalTitleGap, minVerticalPadding, minLeadingWidth
This commit is contained in:
@@ -719,11 +719,17 @@ class ListTile extends StatelessWidget {
|
||||
this.tileColor,
|
||||
this.selectedTileColor,
|
||||
this.enableFeedback,
|
||||
this.horizontalTitleGap = 16.0,
|
||||
this.minVerticalPadding = 4.0,
|
||||
this.minLeadingWidth = 40.0,
|
||||
}) : 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.
|
||||
@@ -922,6 +928,15 @@ class ListTile extends StatelessWidget {
|
||||
/// * [Feedback] for providing platform-specific feedback to certain actions.
|
||||
final bool? enableFeedback;
|
||||
|
||||
/// The horizontal gap between the titles and the leading/trailing widgets.
|
||||
final double horizontalTitleGap;
|
||||
|
||||
/// The minimum padding on the top and bottom of the title and subtitle widgets.
|
||||
final double minVerticalPadding;
|
||||
|
||||
/// The minimum leading width.
|
||||
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.
|
||||
///
|
||||
@@ -1133,6 +1148,9 @@ class ListTile extends StatelessWidget {
|
||||
textDirection: textDirection,
|
||||
titleBaselineType: titleStyle.textBaseline!,
|
||||
subtitleBaselineType: subtitleStyle?.textBaseline,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1161,12 +1179,18 @@ class _ListTile extends RenderObjectWidget {
|
||||
required this.visualDensity,
|
||||
required this.textDirection,
|
||||
required this.titleBaselineType,
|
||||
required this.horizontalTitleGap,
|
||||
required this.minVerticalPadding,
|
||||
required this.minLeadingWidth,
|
||||
this.subtitleBaselineType,
|
||||
}) : assert(isThreeLine != null),
|
||||
assert(isDense != null),
|
||||
assert(visualDensity != null),
|
||||
assert(textDirection != null),
|
||||
assert(titleBaselineType != null),
|
||||
assert(horizontalTitleGap != null),
|
||||
assert(minVerticalPadding != null),
|
||||
assert(minLeadingWidth != null),
|
||||
super(key: key);
|
||||
|
||||
final Widget? leading;
|
||||
@@ -1179,6 +1203,9 @@ class _ListTile extends RenderObjectWidget {
|
||||
final TextDirection textDirection;
|
||||
final TextBaseline titleBaselineType;
|
||||
final TextBaseline? subtitleBaselineType;
|
||||
final double horizontalTitleGap;
|
||||
final double minVerticalPadding;
|
||||
final double minLeadingWidth;
|
||||
|
||||
@override
|
||||
_ListTileElement createElement() => _ListTileElement(this);
|
||||
@@ -1192,6 +1219,9 @@ class _ListTile extends RenderObjectWidget {
|
||||
textDirection: textDirection,
|
||||
titleBaselineType: titleBaselineType,
|
||||
subtitleBaselineType: subtitleBaselineType,
|
||||
horizontalTitleGap: horizontalTitleGap,
|
||||
minVerticalPadding: minVerticalPadding,
|
||||
minLeadingWidth: minLeadingWidth,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1203,7 +1233,10 @@ class _ListTile extends RenderObjectWidget {
|
||||
..visualDensity = visualDensity
|
||||
..textDirection = textDirection
|
||||
..titleBaselineType = titleBaselineType
|
||||
..subtitleBaselineType = subtitleBaselineType;
|
||||
..subtitleBaselineType = subtitleBaselineType
|
||||
..horizontalTitleGap = horizontalTitleGap
|
||||
..minLeadingWidth = minLeadingWidth
|
||||
..minVerticalPadding = minVerticalPadding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1319,23 +1352,26 @@ class _RenderListTile extends RenderBox {
|
||||
required TextDirection textDirection,
|
||||
required TextBaseline titleBaselineType,
|
||||
TextBaseline? subtitleBaselineType,
|
||||
required double horizontalTitleGap,
|
||||
required double minVerticalPadding,
|
||||
required double minLeadingWidth,
|
||||
}) : assert(isDense != null),
|
||||
assert(visualDensity != null),
|
||||
assert(isThreeLine != null),
|
||||
assert(textDirection != null),
|
||||
assert(titleBaselineType != null),
|
||||
assert(horizontalTitleGap != null),
|
||||
assert(minVerticalPadding != null),
|
||||
assert(minLeadingWidth != null),
|
||||
_isDense = isDense,
|
||||
_visualDensity = visualDensity,
|
||||
_isThreeLine = isThreeLine,
|
||||
_textDirection = textDirection,
|
||||
_titleBaselineType = titleBaselineType,
|
||||
_subtitleBaselineType = subtitleBaselineType;
|
||||
|
||||
static const double _minLeadingWidth = 40.0;
|
||||
// The horizontal gap between the titles and the leading/trailing widgets
|
||||
double get _horizontalTitleGap => 16.0 + visualDensity.horizontal * 2.0;
|
||||
// The minimum padding on the top and bottom of the title and subtitle widgets.
|
||||
static const double _minVerticalPadding = 4.0;
|
||||
_subtitleBaselineType = subtitleBaselineType,
|
||||
_horizontalTitleGap = horizontalTitleGap + visualDensity.horizontal * 2.0,
|
||||
_minVerticalPadding = minVerticalPadding,
|
||||
_minLeadingWidth = minLeadingWidth;
|
||||
|
||||
final Map<_ListTileSlot, RenderBox> children = <_ListTileSlot, RenderBox>{};
|
||||
|
||||
@@ -1446,6 +1482,39 @@ class _RenderListTile extends RenderBox {
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
double get horizontalTitleGap => _horizontalTitleGap;
|
||||
double _horizontalTitleGap;
|
||||
|
||||
set horizontalTitleGap(double value) {
|
||||
assert(value != null);
|
||||
if (_horizontalTitleGap == value)
|
||||
return;
|
||||
_horizontalTitleGap = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
double get minVerticalPadding => _minVerticalPadding;
|
||||
double _minVerticalPadding;
|
||||
|
||||
set minVerticalPadding(double value) {
|
||||
assert(value != null);
|
||||
if (_minVerticalPadding == value)
|
||||
return;
|
||||
_minVerticalPadding = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
double get minLeadingWidth => _minLeadingWidth;
|
||||
double _minLeadingWidth;
|
||||
|
||||
set minLeadingWidth(double value) {
|
||||
assert(value != null);
|
||||
if (_minLeadingWidth == value)
|
||||
return;
|
||||
_minLeadingWidth = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
@override
|
||||
void attach(PipelineOwner owner) {
|
||||
super.attach(owner);
|
||||
|
||||
@@ -1834,4 +1834,161 @@ void main() {
|
||||
expect(feedback.hapticCount, 0);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('ListTile horizontalTitleGap = 0.0', (WidgetTester tester) async {
|
||||
Widget buildFrame(TextDirection textDirection) {
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.zero,
|
||||
textScaleFactor: 1.0,
|
||||
),
|
||||
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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
expect(right('title'), 744.0); // horizontalTitleGap: 0
|
||||
});
|
||||
|
||||
testWidgets('ListTile horizontalTitleGap = (default) && ListTile minLeadingWidth = (default)', (WidgetTester tester) async {
|
||||
Widget buildFrame(TextDirection textDirection) {
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.zero,
|
||||
textScaleFactor: 1.0,
|
||||
),
|
||||
child: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: Material(
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: const ListTile(
|
||||
leading: Text('L'),
|
||||
title: Text('title'),
|
||||
trailing: Text('T'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
// horizontalTitleGap: ListTileDefaultValue.horizontalTitleGap (16.0)
|
||||
expect(left('title'), 72.0);
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl));
|
||||
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 56.0));
|
||||
// horizontalTitleGap: ListTileDefaultValue.horizontalTitleGap (16.0)
|
||||
expect(right('title'), 728.0);
|
||||
});
|
||||
|
||||
testWidgets('ListTile minVerticalPadding = 80.0', (WidgetTester tester) async {
|
||||
Widget buildFrame(TextDirection textDirection) {
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.zero,
|
||||
textScaleFactor: 1.0,
|
||||
),
|
||||
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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.ltr));
|
||||
|
||||
// minVerticalPadding: 80.0
|
||||
// 80 + 80 + 16(Title) = 176
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 176.0));
|
||||
|
||||
await tester.pumpWidget(buildFrame(TextDirection.rtl));
|
||||
|
||||
// minVerticalPadding: 80.0
|
||||
// 80 + 80 + 16(Title) = 176
|
||||
expect(tester.getSize(find.byType(ListTile)), const Size(800.0, 176.0));
|
||||
});
|
||||
|
||||
testWidgets('ListTile minLeadingWidth = 60.0', (WidgetTester tester) async {
|
||||
Widget buildFrame(TextDirection textDirection) {
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.zero,
|
||||
textScaleFactor: 1.0,
|
||||
),
|
||||
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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user