Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit cc435d6

Browse files
authored
Fix Color Scheme Defaults in Material 3 (#112666)
1 parent 8469896 commit cc435d6

12 files changed

+359
-32
lines changed

dev/tools/gen_defaults/bin/gen_defaults.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'package:gen_defaults/bottom_sheet_template.dart';
2424
import 'package:gen_defaults/button_template.dart';
2525
import 'package:gen_defaults/card_template.dart';
2626
import 'package:gen_defaults/checkbox_template.dart';
27+
import 'package:gen_defaults/color_scheme_template.dart';
2728
import 'package:gen_defaults/dialog_template.dart';
2829
import 'package:gen_defaults/divider_template.dart';
2930
import 'package:gen_defaults/fab_template.dart';
@@ -127,6 +128,7 @@ Future<void> main(List<String> args) async {
127128
ButtonTemplate('md.comp.text-button', 'TextButton', '$materialLib/text_button.dart', tokens).updateFile();
128129
CardTemplate('Card', '$materialLib/card.dart', tokens).updateFile();
129130
CheckboxTemplate('Checkbox', '$materialLib/checkbox.dart', tokens).updateFile();
131+
ColorSchemeTemplate('ColorScheme', '$materialLib/theme_data.dart', tokens).updateFile();
130132
DialogFullscreenTemplate('DialogFullscreen', '$materialLib/dialog.dart', tokens).updateFile();
131133
DialogTemplate('Dialog', '$materialLib/dialog.dart', tokens).updateFile();
132134
DividerTemplate('Divider', '$materialLib/divider.dart', tokens).updateFile();
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'template.dart';
6+
7+
class ColorSchemeTemplate extends TokenTemplate {
8+
ColorSchemeTemplate(super.blockName, super.fileName, super.tokens);
9+
10+
// Map of light color scheme token data from tokens.
11+
late Map<String, dynamic> colorTokensLight = tokens['colorsLight'] as Map<String, dynamic>;
12+
13+
// Map of dark color scheme token data from tokens.
14+
late Map<String, dynamic> colorTokensDark = tokens['colorsDark'] as Map<String, dynamic>;
15+
16+
@override
17+
String generate() => '''
18+
const ColorScheme _colorSchemeLightM3 = ColorScheme(
19+
brightness: Brightness.light,
20+
primary: Color(${tokens[colorTokensLight['md.sys.color.primary']]}),
21+
onPrimary: Color(${tokens[colorTokensLight['md.sys.color.on-primary']]}),
22+
primaryContainer: Color(${tokens[colorTokensLight['md.sys.color.primary-container']]}),
23+
onPrimaryContainer: Color(${tokens[colorTokensLight['md.sys.color.on-primary-container']]}),
24+
secondary: Color(${tokens[colorTokensLight['md.sys.color.secondary']]}),
25+
onSecondary: Color(${tokens[colorTokensLight['md.sys.color.on-secondary']]}),
26+
secondaryContainer: Color(${tokens[colorTokensLight['md.sys.color.secondary-container']]}),
27+
onSecondaryContainer: Color(${tokens[colorTokensLight['md.sys.color.on-secondary-container']]}),
28+
tertiary: Color(${tokens[colorTokensLight['md.sys.color.tertiary']]}),
29+
onTertiary: Color(${tokens[colorTokensLight['md.sys.color.on-tertiary']]}),
30+
tertiaryContainer: Color(${tokens[colorTokensLight['md.sys.color.tertiary-container']]}),
31+
onTertiaryContainer: Color(${tokens[colorTokensLight['md.sys.color.on-tertiary-container']]}),
32+
error: Color(${tokens[colorTokensLight['md.sys.color.error']]}),
33+
onError: Color(${tokens[colorTokensLight['md.sys.color.on-error']]}),
34+
errorContainer: Color(${tokens[colorTokensLight['md.sys.color.error-container']]}),
35+
onErrorContainer: Color(${tokens[colorTokensLight['md.sys.color.on-error-container']]}),
36+
background: Color(${tokens[colorTokensLight['md.sys.color.background']]}),
37+
onBackground: Color(${tokens[colorTokensLight['md.sys.color.on-background']]}),
38+
surface: Color(${tokens[colorTokensLight['md.sys.color.surface']]}),
39+
onSurface: Color(${tokens[colorTokensLight['md.sys.color.on-surface']]}),
40+
surfaceVariant: Color(${tokens[colorTokensLight['md.sys.color.surface-variant']]}),
41+
onSurfaceVariant: Color(${tokens[colorTokensLight['md.sys.color.on-surface-variant']]}),
42+
outline: Color(${tokens[colorTokensLight['md.sys.color.outline']]}),
43+
outlineVariant: Color(${tokens[colorTokensLight['md.sys.color.outline-variant']]}),
44+
shadow: Color(${tokens[colorTokensLight['md.sys.color.shadow']]}),
45+
scrim: Color(${tokens[colorTokensLight['md.sys.color.scrim']]}),
46+
inverseSurface: Color(${tokens[colorTokensLight['md.sys.color.inverse-surface']]}),
47+
onInverseSurface: Color(${tokens[colorTokensLight['md.sys.color.inverse-on-surface']]}),
48+
inversePrimary: Color(${tokens[colorTokensLight['md.sys.color.inverse-primary']]}),
49+
// The surfaceTint color is set to the same color as the primary.
50+
surfaceTint: Color(${tokens[colorTokensLight['md.sys.color.primary']]}),
51+
);
52+
53+
const ColorScheme _colorSchemeDarkM3 = ColorScheme(
54+
brightness: Brightness.dark,
55+
primary: Color(${tokens[colorTokensDark['md.sys.color.primary']]}),
56+
onPrimary: Color(${tokens[colorTokensDark['md.sys.color.on-primary']]}),
57+
primaryContainer: Color(${tokens[colorTokensDark['md.sys.color.primary-container']]}),
58+
onPrimaryContainer: Color(${tokens[colorTokensDark['md.sys.color.on-primary-container']]}),
59+
secondary: Color(${tokens[colorTokensDark['md.sys.color.secondary']]}),
60+
onSecondary: Color(${tokens[colorTokensDark['md.sys.color.on-secondary']]}),
61+
secondaryContainer: Color(${tokens[colorTokensDark['md.sys.color.secondary-container']]}),
62+
onSecondaryContainer: Color(${tokens[colorTokensDark['md.sys.color.on-secondary-container']]}),
63+
tertiary: Color(${tokens[colorTokensDark['md.sys.color.tertiary']]}),
64+
onTertiary: Color(${tokens[colorTokensDark['md.sys.color.on-tertiary']]}),
65+
tertiaryContainer: Color(${tokens[colorTokensDark['md.sys.color.tertiary-container']]}),
66+
onTertiaryContainer: Color(${tokens[colorTokensDark['md.sys.color.on-tertiary-container']]}),
67+
error: Color(${tokens[colorTokensDark['md.sys.color.error']]}),
68+
onError: Color(${tokens[colorTokensDark['md.sys.color.on-error']]}),
69+
errorContainer: Color(${tokens[colorTokensDark['md.sys.color.error-container']]}),
70+
onErrorContainer: Color(${tokens[colorTokensDark['md.sys.color.on-error-container']]}),
71+
background: Color(${tokens[colorTokensDark['md.sys.color.background']]}),
72+
onBackground: Color(${tokens[colorTokensDark['md.sys.color.on-background']]}),
73+
surface: Color(${tokens[colorTokensDark['md.sys.color.surface']]}),
74+
onSurface: Color(${tokens[colorTokensDark['md.sys.color.on-surface']]}),
75+
surfaceVariant: Color(${tokens[colorTokensDark['md.sys.color.surface-variant']]}),
76+
onSurfaceVariant: Color(${tokens[colorTokensDark['md.sys.color.on-surface-variant']]}),
77+
outline: Color(${tokens[colorTokensDark['md.sys.color.outline']]}),
78+
outlineVariant: Color(${tokens[colorTokensDark['md.sys.color.outline-variant']]}),
79+
shadow: Color(${tokens[colorTokensDark['md.sys.color.shadow']]}),
80+
scrim: Color(${tokens[colorTokensDark['md.sys.color.scrim']]}),
81+
inverseSurface: Color(${tokens[colorTokensDark['md.sys.color.inverse-surface']]}),
82+
onInverseSurface: Color(${tokens[colorTokensDark['md.sys.color.inverse-on-surface']]}),
83+
inversePrimary: Color(${tokens[colorTokensDark['md.sys.color.inverse-primary']]}),
84+
// The surfaceTint color is set to the same color as the primary.
85+
surfaceTint: Color(${tokens[colorTokensDark['md.sys.color.primary']]}),
86+
);
87+
''';
88+
}

packages/flutter/lib/src/material/theme_data.dart

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -478,15 +478,18 @@ class ThemeData with Diagnosticable {
478478
assert(colorSchemeSeed == null || primaryColor == null);
479479
final Brightness effectiveBrightness = brightness ?? colorScheme?.brightness ?? Brightness.light;
480480
final bool isDark = effectiveBrightness == Brightness.dark;
481-
if (colorSchemeSeed != null) {
482-
colorScheme = ColorScheme.fromSeed(seedColor: colorSchemeSeed, brightness: effectiveBrightness);
481+
if (colorSchemeSeed != null || useMaterial3) {
482+
if (colorSchemeSeed != null) {
483+
colorScheme = ColorScheme.fromSeed(seedColor: colorSchemeSeed, brightness: effectiveBrightness);
484+
}
485+
colorScheme ??= isDark ? _colorSchemeDarkM3 : _colorSchemeLightM3;
483486

484487
// For surfaces that use primary color in light themes and surface color in dark
485488
final Color primarySurfaceColor = isDark ? colorScheme.surface : colorScheme.primary;
486489
final Color onPrimarySurfaceColor = isDark ? colorScheme.onSurface : colorScheme.onPrimary;
487490

488491
// Default some of the color settings to values from the color scheme
489-
primaryColor = primarySurfaceColor;
492+
primaryColor ??= primarySurfaceColor;
490493
primaryColorBrightness = ThemeData.estimateBrightnessForColor(primarySurfaceColor);
491494
canvasColor ??= colorScheme.background;
492495
accentColor ??= colorScheme.secondary;
@@ -1243,8 +1246,10 @@ class ThemeData with Diagnosticable {
12431246
/// A temporary flag used to opt-in to Material 3 features.
12441247
///
12451248
/// If true, then widgets that have been migrated to Material 3 will
1246-
/// use new colors, typography and other features of Material 3.
1247-
/// If false, they will use the Material 2 look and feel.
1249+
/// use new colors, typography and other features of Material 3. A new
1250+
/// purple-based [ColorScheme] will be created and applied to the updated
1251+
/// widgets, as long as this is set to true. If false, they will use the
1252+
/// Material 2 look and feel.
12481253
///
12491254
/// During the migration to Material 3, turning this on may yield
12501255
/// inconsistent look and feel in your app as some widgets are migrated
@@ -2950,3 +2955,84 @@ class VisualDensity with Diagnosticable {
29502955
return '${super.toStringShort()}(h: ${debugFormatDouble(horizontal)}, v: ${debugFormatDouble(vertical)})';
29512956
}
29522957
}
2958+
2959+
// BEGIN GENERATED TOKEN PROPERTIES - ColorScheme
2960+
2961+
// Do not edit by hand. The code between the "BEGIN GENERATED" and
2962+
// "END GENERATED" comments are generated from data in the Material
2963+
// Design token database by the script:
2964+
// dev/tools/gen_defaults/bin/gen_defaults.dart.
2965+
2966+
// Token database version: v0_132
2967+
2968+
const ColorScheme _colorSchemeLightM3 = ColorScheme(
2969+
brightness: Brightness.light,
2970+
primary: Color(0xFF6750A4),
2971+
onPrimary: Color(0xFFFFFFFF),
2972+
primaryContainer: Color(0xFFEADDFF),
2973+
onPrimaryContainer: Color(0xFF21005D),
2974+
secondary: Color(0xFF625B71),
2975+
onSecondary: Color(0xFFFFFFFF),
2976+
secondaryContainer: Color(0xFFE8DEF8),
2977+
onSecondaryContainer: Color(0xFF1D192B),
2978+
tertiary: Color(0xFF7D5260),
2979+
onTertiary: Color(0xFFFFFFFF),
2980+
tertiaryContainer: Color(0xFFFFD8E4),
2981+
onTertiaryContainer: Color(0xFF31111D),
2982+
error: Color(0xFFB3261E),
2983+
onError: Color(0xFFFFFFFF),
2984+
errorContainer: Color(0xFFF9DEDC),
2985+
onErrorContainer: Color(0xFF410E0B),
2986+
background: Color(0xFFFFFBFE),
2987+
onBackground: Color(0xFF1C1B1F),
2988+
surface: Color(0xFFFFFBFE),
2989+
onSurface: Color(0xFF1C1B1F),
2990+
surfaceVariant: Color(0xFFE7E0EC),
2991+
onSurfaceVariant: Color(0xFF49454F),
2992+
outline: Color(0xFF79747E),
2993+
outlineVariant: Color(0xFFCAC4D0),
2994+
shadow: Color(0xFF000000),
2995+
scrim: Color(0xFF000000),
2996+
inverseSurface: Color(0xFF313033),
2997+
onInverseSurface: Color(0xFFF4EFF4),
2998+
inversePrimary: Color(0xFFD0BCFF),
2999+
// The surfaceTint color is set to the same color as the primary.
3000+
surfaceTint: Color(0xFF6750A4),
3001+
);
3002+
3003+
const ColorScheme _colorSchemeDarkM3 = ColorScheme(
3004+
brightness: Brightness.dark,
3005+
primary: Color(0xFFD0BCFF),
3006+
onPrimary: Color(0xFF381E72),
3007+
primaryContainer: Color(0xFF4F378B),
3008+
onPrimaryContainer: Color(0xFFEADDFF),
3009+
secondary: Color(0xFFCCC2DC),
3010+
onSecondary: Color(0xFF332D41),
3011+
secondaryContainer: Color(0xFF4A4458),
3012+
onSecondaryContainer: Color(0xFFE8DEF8),
3013+
tertiary: Color(0xFFEFB8C8),
3014+
onTertiary: Color(0xFF492532),
3015+
tertiaryContainer: Color(0xFF633B48),
3016+
onTertiaryContainer: Color(0xFFFFD8E4),
3017+
error: Color(0xFFF2B8B5),
3018+
onError: Color(0xFF601410),
3019+
errorContainer: Color(0xFF8C1D18),
3020+
onErrorContainer: Color(0xFFF9DEDC),
3021+
background: Color(0xFF1C1B1F),
3022+
onBackground: Color(0xFFE6E1E5),
3023+
surface: Color(0xFF1C1B1F),
3024+
onSurface: Color(0xFFE6E1E5),
3025+
surfaceVariant: Color(0xFF49454F),
3026+
onSurfaceVariant: Color(0xFFCAC4D0),
3027+
outline: Color(0xFF938F99),
3028+
outlineVariant: Color(0xFF49454F),
3029+
shadow: Color(0xFF000000),
3030+
scrim: Color(0xFF000000),
3031+
inverseSurface: Color(0xFFE6E1E5),
3032+
onInverseSurface: Color(0xFF313033),
3033+
inversePrimary: Color(0xFF6750A4),
3034+
// The surfaceTint color is set to the same color as the primary.
3035+
surfaceTint: Color(0xFFD0BCFF),
3036+
);
3037+
3038+
// END GENERATED TOKEN PROPERTIES - ColorScheme

packages/flutter/test/material/banner_theme_test.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ void main() {
6767
});
6868

6969
testWidgets('Passing no MaterialBannerThemeData returns defaults', (WidgetTester tester) async {
70-
final ThemeData theme = ThemeData();
70+
final ThemeData theme = ThemeData(useMaterial3: true);
7171
const String contentText = 'Content';
7272

7373
await tester.pumpWidget(MaterialApp(
74-
theme: ThemeData(useMaterial3: true),
74+
theme: theme,
7575
home: Scaffold(
7676
body: MaterialBanner(
7777
content: const Text(contentText),
@@ -87,7 +87,7 @@ void main() {
8787
));
8888

8989
final Material material = _getMaterialFromText(tester, contentText);
90-
expect(material.color, const Color(0xffffffff));
90+
expect(material.color, theme.colorScheme.surface);
9191
expect(material.surfaceTintColor, theme.colorScheme.surfaceTint);
9292
expect(material.shadowColor, null);
9393
expect(material.elevation, 0.0);
@@ -110,16 +110,16 @@ void main() {
110110
expect(leadingTopLeft.dx - materialTopLeft.dx, 16); // Default leading padding.
111111

112112
final Divider divider = tester.widget<Divider>(find.byType(Divider));
113-
expect(divider.color, theme.colorScheme.surfaceVariant);
113+
expect(divider.color, theme.colorScheme.outlineVariant);
114114
});
115115

116116
testWidgets('Passing no MaterialBannerThemeData returns defaults when presented by ScaffoldMessenger', (WidgetTester tester) async {
117-
final ThemeData theme = ThemeData();
117+
final ThemeData theme = ThemeData(useMaterial3: true);
118118
const String contentText = 'Content';
119119
const Key tapTarget = Key('tap-target');
120120

121121
await tester.pumpWidget(MaterialApp(
122-
theme: ThemeData(useMaterial3: true),
122+
theme: theme,
123123
home: Scaffold(
124124
body: Builder(
125125
builder: (BuildContext context) {
@@ -151,7 +151,7 @@ void main() {
151151
await tester.pumpAndSettle();
152152

153153
final Material material = _getMaterialFromText(tester, contentText);
154-
expect(material.color, const Color(0xffffffff));
154+
expect(material.color, theme.colorScheme.surface);
155155
expect(material.surfaceTintColor, theme.colorScheme.surfaceTint);
156156
expect(material.shadowColor, null);
157157
expect(material.elevation, 0.0);
@@ -174,7 +174,7 @@ void main() {
174174
expect(leadingTopLeft.dx - materialTopLeft.dx, 16); // Default leading padding.
175175

176176
final Divider divider = tester.widget<Divider>(find.byType(Divider));
177-
expect(divider.color, theme.colorScheme.surfaceVariant);
177+
expect(divider.color, theme.colorScheme.outlineVariant);
178178
});
179179

180180
testWidgets('MaterialBanner uses values from MaterialBannerThemeData', (WidgetTester tester) async {

packages/flutter/test/material/card_theme_test.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ void main() {
1616
});
1717

1818
testWidgets('Passing no CardTheme returns defaults', (WidgetTester tester) async {
19+
final ThemeData theme = ThemeData(useMaterial3: true);
1920
await tester.pumpWidget(MaterialApp(
20-
theme: ThemeData(useMaterial3: true),
21+
theme: theme,
2122
home: const Scaffold(
2223
body: Card(),
2324
),
@@ -27,9 +28,9 @@ void main() {
2728
final Material material = _getCardMaterial(tester);
2829

2930
expect(material.clipBehavior, Clip.none);
30-
expect(material.color, Colors.white);
31-
expect(material.shadowColor, Colors.black);
32-
expect(material.surfaceTintColor, Colors.blue); // Default primary color
31+
expect(material.color, theme.colorScheme.surface);
32+
expect(material.shadowColor, theme.colorScheme.shadow);
33+
expect(material.surfaceTintColor, theme.colorScheme.surfaceTint); // Default primary color
3334
expect(material.elevation, 1.0);
3435
expect(container.margin, const EdgeInsets.all(4.0));
3536
expect(material.shape, const RoundedRectangleBorder(

packages/flutter/test/material/checkbox_test.dart

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,11 +1587,12 @@ void main() {
15871587

15881588
testWidgets('Checkbox has default error color when isError is set to true - M3', (WidgetTester tester) async {
15891589
final FocusNode focusNode = FocusNode(debugLabel: 'Checkbox');
1590+
final ThemeData themeData = ThemeData(useMaterial3: true);
15901591
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
15911592
bool? value = true;
15921593
Widget buildApp({bool autoFocus = true}) {
15931594
return MaterialApp(
1594-
theme: ThemeData(useMaterial3: true),
1595+
theme: themeData,
15951596
home: Material(
15961597
child: Center(
15971598
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
@@ -1617,7 +1618,7 @@ void main() {
16171618
expect(focusNode.hasPrimaryFocus, isTrue);
16181619
expect(
16191620
Material.of(tester.element(find.byType(Checkbox))),
1620-
paints..circle(color: theme.colorScheme.error.withOpacity(0.12))..path(color: theme.colorScheme.error)..path(color: theme.colorScheme.onError)
1621+
paints..circle(color: themeData.colorScheme.error.withOpacity(0.12))..path(color: themeData.colorScheme.error)..path(color: themeData.colorScheme.onError)
16211622
);
16221623

16231624
// Default color
@@ -1627,7 +1628,7 @@ void main() {
16271628
expect(focusNode.hasPrimaryFocus, isFalse);
16281629
expect(
16291630
Material.of(tester.element(find.byType(Checkbox))),
1630-
paints..path(color: theme.colorScheme.error)..path(color: theme.colorScheme.onError)
1631+
paints..path(color: themeData.colorScheme.error)..path(color: themeData.colorScheme.onError)
16311632
);
16321633

16331634
// Start hovering
@@ -1639,8 +1640,8 @@ void main() {
16391640
expect(
16401641
Material.of(tester.element(find.byType(Checkbox))),
16411642
paints
1642-
..circle(color: theme.colorScheme.error.withOpacity(0.08))
1643-
..path(color: theme.colorScheme.error)
1643+
..circle(color: themeData.colorScheme.error.withOpacity(0.08))
1644+
..path(color: themeData.colorScheme.error)
16441645
);
16451646

16461647
// Start pressing
@@ -1649,8 +1650,8 @@ void main() {
16491650
expect(
16501651
Material.of(tester.element(find.byType(Checkbox))),
16511652
paints
1652-
..circle(color: theme.colorScheme.error.withOpacity(0.12))
1653-
..path(color: theme.colorScheme.error)
1653+
..circle(color: themeData.colorScheme.error.withOpacity(0.12))
1654+
..path(color: themeData.colorScheme.error)
16541655
);
16551656
await gestureLongPress.up();
16561657
await tester.pump();

packages/flutter/test/material/dialog_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ void main() {
134134
await tester.pumpAndSettle();
135135

136136
final Material material3Widget = _getMaterialFromDialog(tester);
137-
expect(material3Widget.color, const Color(0xff424242));
137+
expect(material3Widget.color, material3Theme.colorScheme.surface);
138138
expect(material3Widget.shape, _defaultM3DialogShape);
139139
expect(material3Widget.elevation, 6.0);
140140
});

packages/flutter/test/material/dialog_theme_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ void main() {
269269

270270
// first is Text('X')
271271
final RichText text = tester.widget(find.byType(RichText).last);
272-
expect(text.text.style!.color, ThemeData().colorScheme.secondary);
272+
expect(text.text.style!.color, theme.colorScheme.secondary);
273273
});
274274

275275
testWidgets('Custom Title Text Style - Constructor Param', (WidgetTester tester) async {

0 commit comments

Comments
 (0)