Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CircleAvatar to support Material 3 #114812

Merged
merged 2 commits into from Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 14 additions & 5 deletions packages/flutter/lib/src/material/circle_avatar.dart
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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:
Expand All @@ -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);
Expand Down
91 changes: 59 additions & 32 deletions packages/flutter/test/material/circle_avatar_test.dart
Expand Up @@ -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(
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NICE

// 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 }) {
Expand Down