diff --git a/packages/flutter/lib/src/material/list_tile.dart b/packages/flutter/lib/src/material/list_tile.dart index 9ae0df7d38c4..cc5ec4143689 100644 --- a/packages/flutter/lib/src/material/list_tile.dart +++ b/packages/flutter/lib/src/material/list_tile.dart @@ -593,7 +593,13 @@ class ListTile extends StatelessWidget { return selectedColor ?? tileTheme.selectedColor ?? theme.listTileTheme.selectedColor ?? theme.colorScheme.primary; } - final Color? color = iconColor ?? tileTheme.iconColor ?? theme.listTileTheme.iconColor; + final Color? color = iconColor + ?? tileTheme.iconColor + ?? theme.listTileTheme.iconColor + // If [ThemeData.useMaterial3] is set to true the disabled icon color + // will be set to Theme.colorScheme.onSurface(0.38), if false, defaults to null, + // as described in: https://m3.material.io/components/icon-buttons/specs. + ?? (theme.useMaterial3 ? theme.colorScheme.onSurface.withOpacity(0.38) : null); if (color != null) { return color; } diff --git a/packages/flutter/test/material/list_tile_test.dart b/packages/flutter/test/material/list_tile_test.dart index d9a0790ec3a2..2066f784d825 100644 --- a/packages/flutter/test/material/list_tile_test.dart +++ b/packages/flutter/test/material/list_tile_test.dart @@ -2037,8 +2037,6 @@ void main() { }); testWidgets('selected, enabled ListTile default icon color, light and dark themes', (WidgetTester tester) async { - // Regression test for https://github.com/flutter/flutter/pull/77004 - const ColorScheme lightColorScheme = ColorScheme.light(); const ColorScheme darkColorScheme = ColorScheme.dark(); final Key leadingKey = UniqueKey(); @@ -2048,8 +2046,8 @@ void main() { Widget buildFrame({ required Brightness brightness, required bool selected }) { final ThemeData theme = brightness == Brightness.light - ? ThemeData.from(colorScheme: const ColorScheme.light()) - : ThemeData.from(colorScheme: const ColorScheme.dark()); + ? ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true) + : ThemeData.from(colorScheme: const ColorScheme.dark(), useMaterial3: true); return MaterialApp( theme: theme, home: Material( @@ -2075,10 +2073,10 @@ void main() { expect(iconColor(trailingKey), lightColorScheme.primary); await tester.pumpWidget(buildFrame(brightness: Brightness.light, selected: false)); - expect(iconColor(leadingKey), Colors.black45); - expect(iconColor(titleKey), Colors.black45); - expect(iconColor(subtitleKey), Colors.black45); - expect(iconColor(trailingKey), Colors.black45); + expect(iconColor(leadingKey), lightColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(titleKey), lightColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(subtitleKey), lightColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(trailingKey), lightColorScheme.onSurface.withOpacity(0.38)); await tester.pumpWidget(buildFrame(brightness: Brightness.dark, selected: true)); await tester.pumpAndSettle(); // Animated theme change @@ -2090,10 +2088,10 @@ void main() { // For this configuration, ListTile defers to the default IconTheme. // The default dark theme's IconTheme has color:white await tester.pumpWidget(buildFrame(brightness: Brightness.dark, selected: false)); - expect(iconColor(leadingKey), Colors.white); - expect(iconColor(titleKey), Colors.white); - expect(iconColor(subtitleKey), Colors.white); - expect(iconColor(trailingKey), Colors.white); + expect(iconColor(leadingKey), darkColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(titleKey), darkColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(subtitleKey), darkColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(trailingKey), darkColorScheme.onSurface.withOpacity(0.38)); }); testWidgets('ListTile font size', (WidgetTester tester) async { @@ -2442,6 +2440,66 @@ void main() { trailing = _getTextRenderObject(tester, 'trailing'); expect(trailing.text.style!.color, theme.textTheme.bodyMedium!.color); }); + + testWidgets('selected, enabled ListTile default icon color, light and dark themes', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/pull/77004 + + const ColorScheme lightColorScheme = ColorScheme.light(); + const ColorScheme darkColorScheme = ColorScheme.dark(); + final Key leadingKey = UniqueKey(); + final Key titleKey = UniqueKey(); + final Key subtitleKey = UniqueKey(); + final Key trailingKey = UniqueKey(); + + Widget buildFrame({ required Brightness brightness, required bool selected }) { + final ThemeData theme = brightness == Brightness.light + ? ThemeData.from(colorScheme: const ColorScheme.light()) + : ThemeData.from(colorScheme: const ColorScheme.dark()); + return MaterialApp( + theme: theme, + home: Material( + child: Center( + child: ListTile( + selected: selected, + leading: TestIcon(key: leadingKey), + title: TestIcon(key: titleKey), + subtitle: TestIcon(key: subtitleKey), + trailing: TestIcon(key: trailingKey), + ), + ), + ), + ); + } + + Color iconColor(Key key) => tester.state(find.byKey(key)).iconTheme.color!; + + await tester.pumpWidget(buildFrame(brightness: Brightness.light, selected: true)); + expect(iconColor(leadingKey), lightColorScheme.primary); + expect(iconColor(titleKey), lightColorScheme.primary); + expect(iconColor(subtitleKey), lightColorScheme.primary); + expect(iconColor(trailingKey), lightColorScheme.primary); + + await tester.pumpWidget(buildFrame(brightness: Brightness.light, selected: false)); + expect(iconColor(leadingKey), Colors.black45); + expect(iconColor(titleKey), Colors.black45); + expect(iconColor(subtitleKey), Colors.black45); + expect(iconColor(trailingKey), Colors.black45); + + await tester.pumpWidget(buildFrame(brightness: Brightness.dark, selected: true)); + await tester.pumpAndSettle(); // Animated theme change + expect(iconColor(leadingKey), darkColorScheme.primary); + expect(iconColor(titleKey), darkColorScheme.primary); + expect(iconColor(subtitleKey), darkColorScheme.primary); + expect(iconColor(trailingKey), darkColorScheme.primary); + + // For this configuration, ListTile defers to the default IconTheme. + // The default dark theme's IconTheme has color:white + await tester.pumpWidget(buildFrame(brightness: Brightness.dark, selected: false)); + expect(iconColor(leadingKey), Colors.white); + expect(iconColor(titleKey), Colors.white); + expect(iconColor(subtitleKey), Colors.white); + expect(iconColor(trailingKey), Colors.white); + }); }); }