From d3dcd7d532b61f43118ab8031bf732aea4a7a9f5 Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Wed, 9 Nov 2022 13:50:41 +0200 Subject: [PATCH] Update `CircleAvatar` to support Material 3 (#114812) --- .../lib/src/material/circle_avatar.dart | 19 +++- .../test/material/circle_avatar_test.dart | 91 ++++++++++++------- 2 files changed, 73 insertions(+), 37 deletions(-) diff --git a/packages/flutter/lib/src/material/circle_avatar.dart b/packages/flutter/lib/src/material/circle_avatar.dart index 61faac64fd8f..11775c4500c4 100644 --- a/packages/flutter/lib/src/material/circle_avatar.dart +++ b/packages/flutter/lib/src/material/circle_avatar.dart @@ -84,7 +84,8 @@ class CircleAvatar extends StatelessWidget { /// The color with which to fill the circle. Changing the background /// color will cause the avatar to animate to the new color. /// - /// If a [backgroundColor] is not specified, the theme's + /// If a [backgroundColor] is not specified and [ThemeData.useMaterial3] is true, + /// [ColorScheme.primaryContainer] will be used, otherwise the theme's /// [ThemeData.primaryColorLight] is used with dark foreground colors, and /// [ThemeData.primaryColorDark] with light foreground colors. final Color? backgroundColor; @@ -94,7 +95,9 @@ class CircleAvatar extends StatelessWidget { /// Defaults to the primary text theme color if no [backgroundColor] is /// specified. /// - /// Defaults to [ThemeData.primaryColorLight] for dark background colors, and + /// If a [foregroundColor] is not specified and [ThemeData.useMaterial3] is true, + /// [ColorScheme.onPrimaryContainer] will be used, otherwise the theme's + /// [ThemeData.primaryColorLight] for dark background colors, and /// [ThemeData.primaryColorDark] for light background colors. final Color? foregroundColor; @@ -192,8 +195,14 @@ class CircleAvatar extends StatelessWidget { Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); final ThemeData theme = Theme.of(context); - TextStyle textStyle = theme.primaryTextTheme.titleMedium!.copyWith(color: foregroundColor); - Color? effectiveBackgroundColor = backgroundColor; + final Color? effectiveForegroundColor = foregroundColor + ?? (theme.useMaterial3 ? theme.colorScheme.onPrimaryContainer : null); + final TextStyle effectiveTextStyle = theme.useMaterial3 + ? theme.textTheme.titleMedium! + : theme.primaryTextTheme.titleMedium!; + TextStyle textStyle = effectiveTextStyle.copyWith(color: effectiveForegroundColor); + Color? effectiveBackgroundColor = backgroundColor + ?? (theme.useMaterial3 ? theme.colorScheme.primaryContainer : null); if (effectiveBackgroundColor == null) { switch (ThemeData.estimateBrightnessForColor(textStyle.color!)) { case Brightness.dark: @@ -203,7 +212,7 @@ class CircleAvatar extends StatelessWidget { effectiveBackgroundColor = theme.primaryColorDark; break; } - } else if (foregroundColor == null) { + } else if (effectiveForegroundColor == null) { switch (ThemeData.estimateBrightnessForColor(backgroundColor!)) { case Brightness.dark: textStyle = textStyle.copyWith(color: theme.primaryColorLight); diff --git a/packages/flutter/test/material/circle_avatar_test.dart b/packages/flutter/test/material/circle_avatar_test.dart index 4a7f53480715..9249f4344bb1 100644 --- a/packages/flutter/test/material/circle_avatar_test.dart +++ b/packages/flutter/test/material/circle_avatar_test.dart @@ -144,36 +144,8 @@ void main() { expect(paragraph.text.style!.color, equals(foregroundColor)); }); - testWidgets('CircleAvatar with light theme', (WidgetTester tester) async { - final ThemeData theme = ThemeData( - primaryColor: Colors.grey.shade100, - primaryColorBrightness: Brightness.light, - ); - await tester.pumpWidget( - wrap( - child: Theme( - data: theme, - child: const CircleAvatar( - child: Text('Z'), - ), - ), - ), - ); - - final RenderConstrainedBox box = tester.renderObject(find.byType(CircleAvatar)); - final RenderDecoratedBox child = box.child! as RenderDecoratedBox; - final BoxDecoration decoration = child.decoration as BoxDecoration; - expect(decoration.color, equals(theme.primaryColorLight)); - - final RenderParagraph paragraph = tester.renderObject(find.text('Z')); - expect(paragraph.text.style!.color, equals(theme.primaryTextTheme.titleLarge!.color)); - }); - - testWidgets('CircleAvatar with dark theme', (WidgetTester tester) async { - final ThemeData theme = ThemeData( - primaryColor: Colors.grey.shade800, - primaryColorBrightness: Brightness.dark, - ); + testWidgets('CircleAvatar default colors', (WidgetTester tester) async { + final ThemeData theme = ThemeData(useMaterial3: true); await tester.pumpWidget( wrap( child: Theme( @@ -188,10 +160,10 @@ void main() { final RenderConstrainedBox box = tester.renderObject(find.byType(CircleAvatar)); final RenderDecoratedBox child = box.child! as RenderDecoratedBox; final BoxDecoration decoration = child.decoration as BoxDecoration; - expect(decoration.color, equals(theme.primaryColorDark)); + expect(decoration.color, equals(theme.colorScheme.primaryContainer)); final RenderParagraph paragraph = tester.renderObject(find.text('Z')); - expect(paragraph.text.style!.color, equals(theme.primaryTextTheme.titleLarge!.color)); + expect(paragraph.text.style!.color, equals(theme.colorScheme.onPrimaryContainer)); }); testWidgets('CircleAvatar text does not expand with textScaleFactor', (WidgetTester tester) async { @@ -306,6 +278,61 @@ void main() { final RenderParagraph paragraph = tester.renderObject(find.text('Z')); expect(paragraph.text.style!.color, equals(ThemeData.fallback().primaryColorLight)); }); + + group('Material 2', () { + // Tests that are only relevant for Material 2. Once ThemeData.useMaterial3 + // is turned on by default, these tests can be removed. + + testWidgets('CircleAvatar default colors with light theme', (WidgetTester tester) async { + final ThemeData theme = ThemeData( + primaryColor: Colors.grey.shade100, + primaryColorBrightness: Brightness.light, + ); + await tester.pumpWidget( + wrap( + child: Theme( + data: theme, + child: const CircleAvatar( + child: Text('Z'), + ), + ), + ), + ); + + final RenderConstrainedBox box = tester.renderObject(find.byType(CircleAvatar)); + final RenderDecoratedBox child = box.child! as RenderDecoratedBox; + final BoxDecoration decoration = child.decoration as BoxDecoration; + expect(decoration.color, equals(theme.primaryColorLight)); + + final RenderParagraph paragraph = tester.renderObject(find.text('Z')); + expect(paragraph.text.style!.color, equals(theme.primaryTextTheme.titleLarge!.color)); + }); + + testWidgets('CircleAvatar default colors with dark theme', (WidgetTester tester) async { + final ThemeData theme = ThemeData( + primaryColor: Colors.grey.shade800, + primaryColorBrightness: Brightness.dark, + ); + await tester.pumpWidget( + wrap( + child: Theme( + data: theme, + child: const CircleAvatar( + child: Text('Z'), + ), + ), + ), + ); + + final RenderConstrainedBox box = tester.renderObject(find.byType(CircleAvatar)); + final RenderDecoratedBox child = box.child! as RenderDecoratedBox; + final BoxDecoration decoration = child.decoration as BoxDecoration; + expect(decoration.color, equals(theme.primaryColorDark)); + + final RenderParagraph paragraph = tester.renderObject(find.text('Z')); + expect(paragraph.text.style!.color, equals(theme.primaryTextTheme.titleLarge!.color)); + }); + }); } Widget wrap({ required Widget child }) {