Update `CircleAvatar` to support Material 3 (#114812)
diff --git a/packages/flutter/lib/src/material/circle_avatar.dart b/packages/flutter/lib/src/material/circle_avatar.dart
index 61faac6..11775c4 100644
--- a/packages/flutter/lib/src/material/circle_avatar.dart
+++ b/packages/flutter/lib/src/material/circle_avatar.dart
@@ -84,7 +84,8 @@
/// 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 @@
/// 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 @@
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 @@
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 4a7f534..9249f43 100644
--- a/packages/flutter/test/material/circle_avatar_test.dart
+++ b/packages/flutter/test/material/circle_avatar_test.dart
@@ -144,11 +144,8 @@
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,
- );
+ testWidgets('CircleAvatar default colors', (WidgetTester tester) async {
+ final ThemeData theme = ThemeData(useMaterial3: true);
await tester.pumpWidget(
wrap(
child: Theme(
@@ -163,35 +160,10 @@
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));
+ 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));
- });
-
- testWidgets('CircleAvatar 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));
+ expect(paragraph.text.style!.color, equals(theme.colorScheme.onPrimaryContainer));
});
testWidgets('CircleAvatar text does not expand with textScaleFactor', (WidgetTester tester) async {
@@ -306,6 +278,61 @@
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 }) {