diff --git a/packages/flutter/lib/src/material/list_tile.dart b/packages/flutter/lib/src/material/list_tile.dart index 113411be92e766..cb327501627835 100644 --- a/packages/flutter/lib/src/material/list_tile.dart +++ b/packages/flutter/lib/src/material/list_tile.dart @@ -658,6 +658,8 @@ class ListTile extends StatelessWidget { this.hoverColor, this.focusNode, this.autofocus = false, + this.tileColor, + this.selectedTileColor, }) : assert(isThreeLine != null), assert(enabled != null), assert(selected != null), @@ -808,6 +810,16 @@ class ListTile extends StatelessWidget { /// {@macro flutter.widgets.Focus.autofocus} final bool autofocus; + /// Defines the background color of `ListTile when [selected] is false. + /// + /// By default, the value of `tileColor` is [Colors.transparent]. + final Color tileColor; + + /// Defines the background color of `ListTile` when [selected] is true. + /// + /// By default, the value of `selectedListColor` is [Colors.transparent]. + final Color selectedTileColor; + /// Add a one pixel border in between each tile. If color isn't specified the /// [ThemeData.dividerColor] of the context's [Theme] is used. /// @@ -913,6 +925,16 @@ class ListTile extends StatelessWidget { : style.copyWith(color: color); } + Color _tileBackgroundColor() { + if (!selected && tileColor != null) + return tileColor; + + if (selected && selectedTileColor != null) + return selectedTileColor; + + return Colors.transparent; + } + @override Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); @@ -984,21 +1006,24 @@ class ListTile extends StatelessWidget { child: Semantics( selected: selected, enabled: enabled, - child: SafeArea( - top: false, - bottom: false, - minimum: resolvedContentPadding, - child: _ListTile( - leading: leadingIcon, - title: titleText, - subtitle: subtitleText, - trailing: trailingIcon, - isDense: _isDenseLayout(tileTheme), - visualDensity: visualDensity ?? theme.visualDensity, - isThreeLine: isThreeLine, - textDirection: textDirection, - titleBaselineType: titleStyle.textBaseline, - subtitleBaselineType: subtitleStyle?.textBaseline, + child: ColoredBox( + color: _tileBackgroundColor(), + child: SafeArea( + top: false, + bottom: false, + minimum: resolvedContentPadding, + child: _ListTile( + leading: leadingIcon, + title: titleText, + subtitle: subtitleText, + trailing: trailingIcon, + isDense: _isDenseLayout(tileTheme), + visualDensity: visualDensity ?? theme.visualDensity, + isThreeLine: isThreeLine, + textDirection: textDirection, + titleBaselineType: titleStyle.textBaseline, + subtitleBaselineType: subtitleStyle?.textBaseline, + ), ), ), ), diff --git a/packages/flutter/test/material/list_tile_test.dart b/packages/flutter/test/material/list_tile_test.dart index 5bc27d661367fe..13d398bd6b27c7 100644 --- a/packages/flutter/test/material/list_tile_test.dart +++ b/packages/flutter/test/material/list_tile_test.dart @@ -1534,4 +1534,80 @@ void main() { expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic); }); + + testWidgets('ListTile respects tileColor & selectedTileColor', (WidgetTester tester) async { + bool isSelected = false; + const Color selectedTileColor = Colors.red; + const Color tileColor = Colors.green; + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return ListTile( + selected: isSelected, + selectedTileColor: selectedTileColor, + tileColor: tileColor, + onTap: () { + setState(()=> isSelected = !isSelected); + }, + title: const Text('Title'), + ); + }, + ), + ), + ), + ), + ); + + // Initially, when isSelected is false, the ListTile should respect tileColor. + ColoredBox coloredBox = tester.widget(find.byType(ColoredBox)); + expect(coloredBox.color, tileColor); + + // Tap on tile to change isSelected. + await tester.tap(find.byType(ListTile)); + await tester.pumpAndSettle(); + + // When isSelected is true, the ListTile should respect selectedTileColor. + coloredBox = tester.widget(find.byType(ColoredBox)); + expect(coloredBox.color, selectedTileColor); + }); + + testWidgets('ListTile default tile color', (WidgetTester tester) async { + bool isSelected = false; + const Color defaultColor = Colors.transparent; + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Center( + child: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return ListTile( + selected: isSelected, + onTap: () { + setState(()=> isSelected = !isSelected); + }, + title: const Text('Title'), + ); + }, + ), + ), + ), + ), + ); + + ColoredBox coloredBox = tester.widget(find.byType(ColoredBox)); + expect(coloredBox.color, defaultColor); + + // Tap on tile to change isSelected. + await tester.tap(find.byType(ListTile)); + await tester.pumpAndSettle(); + + coloredBox = tester.widget(find.byType(ColoredBox)); + expect(isSelected, isTrue); + expect(coloredBox.color, defaultColor); + }); }