From 4c8fc7817be95aa182e0f323ac99cb546c62f2b9 Mon Sep 17 00:00:00 2001 From: Darren Austin Date: Tue, 6 Aug 2019 14:25:34 -0700 Subject: [PATCH] Added ThemeData.from() method to construct a Theme from a ColorScheme (#37355) Provide a new ThemeData.from() method that constructs a Theme based off the colors in a given ColorScheme. --- .../flutter/lib/src/material/theme_data.dart | 68 +++++++++++++++++-- .../test/material/theme_data_test.dart | 34 ++++++++++ 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index dd1f338c7a..ab6eb8e1df 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -221,7 +221,7 @@ class ThemeData extends Diagnosticable { backgroundColor ??= isDark ? Colors.grey[700] : primarySwatch[200]; dialogBackgroundColor ??= isDark ? Colors.grey[800] : Colors.white; indicatorColor ??= accentColor == primaryColor ? Colors.white : accentColor; - hintColor ??= isDark ? const Color(0x80FFFFFF) : const Color(0x8A000000); + hintColor ??= isDark ? const Color(0x80FFFFFF) : const Color(0x8A000000); errorColor ??= Colors.red[700]; inputDecorationTheme ??= const InputDecorationTheme(); pageTransitionsTheme ??= const PageTransitionsTheme(); @@ -476,9 +476,65 @@ class ThemeData extends Diagnosticable { assert(popupMenuTheme != null), assert(bannerTheme != null); - // Warning: make sure these properties are in the exact same order as in - // hashValues() and in the raw constructor and in the order of fields in - // the class and in the lerp() method. + /// Create a [ThemeData] based on the colors in the given [colorScheme] and + /// text styles of the optional [textTheme]. + /// + /// The [colorScheme] can not be null. + /// + /// If [colorScheme.brightness] is [Brightness.dark] then + /// [ThemeData.applyElevationOverlayColor] will be set to true to support + /// the Material dark theme method for indicating elevation by overlaying + /// a semi-transparent white color on top of the surface color. + /// + /// This is the recommended method to theme your application. As we move + /// forward we will be converting all the widget implementations to only use + /// colors or colors derived from those in [ColorScheme]. + /// + /// {@tool sample} + /// This example will set up an application to use the baseline Material + /// Design light and dark themes. + /// + /// ```dart + /// MaterialApp( + /// theme: ThemeData.from(colorScheme: ColorScheme.light()), + /// darkTheme: ThemeData.from(colorScheme: ColorScheme.dark()), + /// ) + /// ``` + /// {@end-tool} + /// + /// See for + /// more discussion on how to pick the right colors. + factory ThemeData.from({ + @required ColorScheme colorScheme, + TextTheme textTheme, + }) { + assert(colorScheme != null); + + final bool isDark = colorScheme.brightness == Brightness.dark; + + // For surfaces that use primary color in light themes and surface color in dark + final Color primarySurfaceColor = isDark ? colorScheme.surface : colorScheme.primary; + final Color onPrimarySurfaceColor = isDark ? colorScheme.onSurface : colorScheme.onPrimary; + + return ThemeData( + brightness: colorScheme.brightness, + primaryColor: primarySurfaceColor, + primaryColorBrightness: ThemeData.estimateBrightnessForColor(primarySurfaceColor), + canvasColor: colorScheme.background, + accentColor: colorScheme.secondary, + accentColorBrightness: ThemeData.estimateBrightnessForColor(colorScheme.secondary), + scaffoldBackgroundColor: colorScheme.background, + cardColor: colorScheme.surface, + dividerColor: colorScheme.onSurface.withOpacity(0.12), + backgroundColor: colorScheme.background, + dialogBackgroundColor: colorScheme.background, + errorColor: colorScheme.error, + textTheme: textTheme, + indicatorColor: onPrimarySurfaceColor, + applyElevationOverlayColor: isDark, + colorScheme: colorScheme, + ); + } /// A default light blue theme. /// @@ -503,6 +559,10 @@ class ThemeData extends Diagnosticable { /// text geometry. factory ThemeData.fallback() => ThemeData.light(); + // Warning: make sure these properties are in the exact same order as in + // hashValues() and in the raw constructor and in the order of fields in + // the class and in the lerp() method. + /// The brightness of the overall theme of the application. Used by widgets /// like buttons to determine what color to pick when not using the primary or /// accent color. diff --git a/packages/flutter/test/material/theme_data_test.dart b/packages/flutter/test/material/theme_data_test.dart index d191ff434b..dac450f99e 100644 --- a/packages/flutter/test/material/theme_data_test.dart +++ b/packages/flutter/test/material/theme_data_test.dart @@ -136,4 +136,38 @@ void main() { test('cursorColor', () { expect(ThemeData(cursorColor: Colors.red).cursorColor, Colors.red); }); + + testWidgets('ThemeData.from a light color scheme sets appropriate values', (WidgetTester tester) async { + const ColorScheme lightColors = ColorScheme.light(); + final ThemeData theme = ThemeData.from(colorScheme: lightColors); + + expect(theme.brightness, equals(Brightness.light)); + expect(theme.primaryColor, equals(lightColors.primary)); + expect(theme.accentColor, equals(lightColors.secondary)); + expect(theme.cardColor, equals(lightColors.surface)); + expect(theme.backgroundColor, equals(lightColors.background)); + expect(theme.canvasColor, equals(lightColors.background)); + expect(theme.scaffoldBackgroundColor, equals(lightColors.background)); + expect(theme.dialogBackgroundColor, equals(lightColors.background)); + expect(theme.errorColor, equals(lightColors.error)); + expect(theme.applyElevationOverlayColor, isFalse); + }); + + testWidgets('ThemeData.from a dark color scheme sets appropriate values', (WidgetTester tester) async { + const ColorScheme darkColors = ColorScheme.dark(); + final ThemeData theme = ThemeData.from(colorScheme: darkColors); + + expect(theme.brightness, equals(Brightness.dark)); + // in dark theme's the color used for main components is surface instead of primary + expect(theme.primaryColor, equals(darkColors.surface)); + expect(theme.accentColor, equals(darkColors.secondary)); + expect(theme.cardColor, equals(darkColors.surface)); + expect(theme.backgroundColor, equals(darkColors.background)); + expect(theme.canvasColor, equals(darkColors.background)); + expect(theme.scaffoldBackgroundColor, equals(darkColors.background)); + expect(theme.dialogBackgroundColor, equals(darkColors.background)); + expect(theme.errorColor, equals(darkColors.error)); + expect(theme.applyElevationOverlayColor, isTrue); + }); + }