theme_data.dart 11.7 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'dart:ui' show Color, hashValues, hashList, lerpDouble;
6

7
import 'colors.dart';
Adam Barth's avatar
Adam Barth committed
8 9
import 'icon_theme_data.dart';
import 'typography.dart';
10 11 12

enum ThemeBrightness { dark, light }

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// Deriving these values is black magic. The spec claims that pressed buttons
// have a highlight of 0x66999999, but that's clearly wrong. The videos in the
// spec show that buttons have a composited highlight of #E1E1E1 on a background
// of #FAFAFA. Assuming that the highlight really has an opacity of 0x66, we can
// solve for the actual color of the highlight:
const Color _kLightThemeHighlightColor = const Color(0x66BCBCBC);

// The same video shows the splash compositing to #D7D7D7 on a background of
// #E1E1E1. Again, assuming the splash has an opacity of 0x66, we can solve for
// the actual color of the splash:
const Color _kLightThemeSplashColor = const Color(0x66C8C8C8);

// Unfortunately, a similar video isn't available for the dark theme, which
// means we assume the values in the spec are actually correct.
const Color _kDarkThemeHighlightColor = const Color(0x40CCCCCC);
const Color _kDarkThemeSplashColor = const Color(0x40CCCCCC);

30 31
class ThemeData {

32 33 34 35
  ThemeData.raw({
    this.brightness,
    this.primaryColor,
    this.primaryColorBrightness,
36 37
    this.accentColor,
    this.accentColorBrightness,
38 39 40 41 42 43 44
    this.canvasColor,
    this.cardColor,
    this.dividerColor,
    this.highlightColor,
    this.splashColor,
    this.unselectedColor,
    this.disabledColor,
45 46 47
    this.buttonColor,
    this.selectionColor,
    this.backgroundColor,
48 49 50
    this.indicatorColor,
    this.hintColor,
    this.hintOpacity,
51
    this.errorColor,
52 53 54 55 56 57 58
    this.text,
    this.primaryTextTheme,
    this.primaryIconTheme
  }) {
    assert(brightness != null);
    assert(primaryColor != null);
    assert(primaryColorBrightness != null);
59 60
    assert(accentColor != null);
    assert(accentColorBrightness != null);
61 62 63 64 65 66 67
    assert(canvasColor != null);
    assert(cardColor != null);
    assert(dividerColor != null);
    assert(highlightColor != null);
    assert(splashColor != null);
    assert(unselectedColor != null);
    assert(disabledColor != null);
68 69 70
    assert(buttonColor != null);
    assert(selectionColor != null);
    assert(disabledColor != null);
71 72 73
    assert(indicatorColor != null);
    assert(hintColor != null);
    assert(hintOpacity != null);
74
    assert(errorColor != null);
75 76 77 78 79 80
    assert(text != null);
    assert(primaryTextTheme != null);
    assert(primaryIconTheme != null);
  }

  factory ThemeData({
81
    ThemeBrightness brightness,
82
    Map<int, Color> primarySwatch,
83 84
    Color primaryColor,
    ThemeBrightness primaryColorBrightness,
85 86
    Color accentColor,
    ThemeBrightness accentColorBrightness,
87 88 89 90 91 92 93
    Color canvasColor,
    Color cardColor,
    Color dividerColor,
    Color highlightColor,
    Color splashColor,
    Color unselectedColor,
    Color disabledColor,
94 95 96
    Color buttonColor,
    Color selectionColor,
    Color backgroundColor,
97 98 99
    Color indicatorColor,
    Color hintColor,
    double hintOpacity,
100
    Color errorColor,
101 102 103 104
    TextTheme text,
    TextTheme primaryTextTheme,
    IconThemeData primaryIconTheme
  }) {
105 106 107 108 109 110 111
    brightness ??= ThemeBrightness.light;
    final bool isDark = brightness == ThemeBrightness.dark;
    primarySwatch ??= Colors.blue;
    primaryColor ??= isDark ? Colors.grey[900] : primarySwatch[500];
    primaryColorBrightness ??= ThemeBrightness.dark;
    accentColor ??= primarySwatch[500];
    accentColorBrightness ??= ThemeBrightness.dark;
112 113 114 115 116 117 118
    canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50];
    cardColor ??= isDark ? Colors.grey[800] : Colors.white;
    dividerColor ??= isDark ? const Color(0x1FFFFFFF) : const Color(0x1F000000);
    highlightColor ??= isDark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor;
    splashColor ??= isDark ? _kDarkThemeSplashColor : _kLightThemeSplashColor;
    unselectedColor ??= isDark ? Colors.white70 : Colors.black54;
    disabledColor ??= isDark ? Colors.white30 : Colors.black26;
119 120 121
    buttonColor ??= isDark ? primarySwatch[600] : Colors.grey[300];
    selectionColor ??= isDark ? accentColor : primarySwatch[200];
    backgroundColor ??= isDark ? Colors.grey[700] : primarySwatch[200];
122 123 124
    indicatorColor ??= accentColor == primaryColor ? Colors.white : accentColor;
    hintColor ??= isDark ? const Color(0x42FFFFFF) : const Color(0x4C000000);
    hintOpacity ??= hintColor != null ? hintColor.alpha / 0xFF : isDark ? 0.26 : 0.30;
125
    errorColor ??= Colors.red[700];
126 127
    text ??= isDark ? Typography.white : Typography.black;
    primaryTextTheme ??= primaryColorBrightness == ThemeBrightness.dark ? Typography.white : Typography.black;
Adam Barth's avatar
Adam Barth committed
128
    primaryIconTheme ??= primaryColorBrightness == ThemeBrightness.dark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
129 130 131 132
    return new ThemeData.raw(
      brightness: brightness,
      primaryColor: primaryColor,
      primaryColorBrightness: primaryColorBrightness,
133 134
      accentColor: accentColor,
      accentColorBrightness: accentColorBrightness,
135 136 137 138 139 140 141
      canvasColor: canvasColor,
      cardColor: cardColor,
      dividerColor: dividerColor,
      highlightColor: highlightColor,
      splashColor: splashColor,
      unselectedColor: unselectedColor,
      disabledColor: disabledColor,
142 143 144
      buttonColor: buttonColor,
      selectionColor: selectionColor,
      backgroundColor: backgroundColor,
145 146 147
      indicatorColor: indicatorColor,
      hintColor: hintColor,
      hintOpacity: hintOpacity,
148
      errorColor: errorColor,
149 150 151 152
      text: text,
      primaryTextTheme: primaryTextTheme,
      primaryIconTheme: primaryIconTheme
    );
153 154
  }

155
  factory ThemeData.light() => new ThemeData(brightness: ThemeBrightness.light);
156 157 158
  factory ThemeData.dark() => new ThemeData(brightness: ThemeBrightness.dark);
  factory ThemeData.fallback() => new ThemeData.light();

159 160 161
  /// 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.
162 163 164 165 166
  ///
  /// When the ThemeBrightness is dark, the canvas, card, and primary colors are
  /// all dark. When the ThemeBrightness is light, the canvas and card colors
  /// are bright, and the primary color's darkness varies as described by
  /// primaryColorBrightness. The primaryColor does not contrast well with the
167
  /// card and canvas colors when the brightness is dark; when the brightness is
168
  /// dark, use Colors.white or the accentColor for a contrasting color.
169
  final ThemeBrightness brightness;
170 171

  /// The background colour for major parts of the app (toolbars, tab bars, etc)
172
  final Color primaryColor;
173

174 175
  /// The brightness of the primaryColor. Used to determine the colour of text and
  /// icons placed on top of the primary color (e.g. toolbar text).
176 177
  final ThemeBrightness primaryColorBrightness;

178
  /// The foreground color for widgets (knobs, text, etc)
179
  final Color accentColor;
180

181 182 183
  /// The brightness of the accentColor. Used to determine the colour of text
  /// and icons placed on top of the accent color (e.g. the icons on a floating
  /// action button).
184
  final ThemeBrightness accentColorBrightness;
185

186 187 188 189 190 191 192 193 194 195 196
  final Color canvasColor;
  final Color cardColor;
  final Color dividerColor;
  final Color highlightColor;
  final Color splashColor;
  final Color unselectedColor;
  final Color disabledColor;
  final Color buttonColor;
  final Color selectionColor;
  final Color backgroundColor;

197 198 199 200 201 202 203
  /// The color of the selected tab indicator in a tab strip.
  final Color indicatorColor;

  // Some users want the pre-multiplied color, others just want the opacity.
  final Color hintColor;
  final double hintOpacity;

204 205 206
  /// The color to use for input validation errors.
  final Color errorColor;

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  /// Text with a color that contrasts with the card and canvas colors.
  final TextTheme text;

  /// A text theme that contrasts with the primary color.
  final TextTheme primaryTextTheme;

  final IconThemeData primaryIconTheme;

  static ThemeData lerp(ThemeData begin, ThemeData end, double t) {
    return new ThemeData.raw(
      brightness: t < 0.5 ? begin.brightness : end.brightness,
      primaryColor: Color.lerp(begin.primaryColor, end.primaryColor, t),
      primaryColorBrightness: t < 0.5 ? begin.primaryColorBrightness : end.primaryColorBrightness,
      canvasColor: Color.lerp(begin.canvasColor, end.canvasColor, t),
      cardColor: Color.lerp(begin.cardColor, end.cardColor, t),
      dividerColor: Color.lerp(begin.dividerColor, end.dividerColor, t),
      highlightColor: Color.lerp(begin.highlightColor, end.highlightColor, t),
      splashColor: Color.lerp(begin.splashColor, end.splashColor, t),
      unselectedColor: Color.lerp(begin.unselectedColor, end.unselectedColor, t),
      disabledColor: Color.lerp(begin.disabledColor, end.disabledColor, t),
227 228 229
      buttonColor: Color.lerp(begin.buttonColor, end.buttonColor, t),
      selectionColor: Color.lerp(begin.selectionColor, end.selectionColor, t),
      backgroundColor: Color.lerp(begin.backgroundColor, end.backgroundColor, t),
230 231 232 233 234
      accentColor: Color.lerp(begin.accentColor, end.accentColor, t),
      accentColorBrightness: t < 0.5 ? begin.accentColorBrightness : end.accentColorBrightness,
      indicatorColor: Color.lerp(begin.indicatorColor, end.indicatorColor, t),
      hintColor: Color.lerp(begin.hintColor, end.hintColor, t),
      hintOpacity: lerpDouble(begin.hintOpacity, end.hintOpacity, t),
235
      errorColor: Color.lerp(begin.errorColor, end.errorColor, t),
236 237 238 239 240 241
      text: TextTheme.lerp(begin.text, end.text, t),
      primaryTextTheme: TextTheme.lerp(begin.primaryTextTheme, end.primaryTextTheme, t),
      primaryIconTheme: IconThemeData.lerp(begin.primaryIconTheme, end.primaryIconTheme, t)
    );
  }

242 243 244 245 246
  bool operator==(Object other) {
    if (other.runtimeType != runtimeType)
      return false;
    ThemeData otherData = other;
    return (otherData.brightness == brightness) &&
247 248
           (otherData.primaryColor == primaryColor) &&
           (otherData.primaryColorBrightness == primaryColorBrightness) &&
249 250 251 252
           (otherData.canvasColor == canvasColor) &&
           (otherData.cardColor == cardColor) &&
           (otherData.dividerColor == dividerColor) &&
           (otherData.highlightColor == highlightColor) &&
253 254 255
           (otherData.splashColor == splashColor) &&
           (otherData.unselectedColor == unselectedColor) &&
           (otherData.disabledColor == disabledColor) &&
256 257 258
           (otherData.buttonColor == buttonColor) &&
           (otherData.selectionColor == selectionColor) &&
           (otherData.backgroundColor == backgroundColor) &&
259 260 261 262
           (otherData.accentColor == accentColor) &&
           (otherData.accentColorBrightness == accentColorBrightness) &&
           (otherData.indicatorColor == indicatorColor) &&
           (otherData.hintColor == hintColor) &&
263
           (otherData.hintOpacity == hintOpacity) &&
264
           (otherData.errorColor == errorColor) &&
265
           (otherData.text == text) &&
266 267
           (otherData.primaryTextTheme == primaryTextTheme) &&
           (otherData.primaryIconTheme == primaryIconTheme);
268 269
  }
  int get hashCode {
270 271
    return hashValues(
      brightness,
272 273
      primaryColor,
      primaryColorBrightness,
274 275 276 277
      canvasColor,
      cardColor,
      dividerColor,
      highlightColor,
278 279 280
      splashColor,
      unselectedColor,
      disabledColor,
281 282 283
      buttonColor,
      selectionColor,
      backgroundColor,
284 285
      accentColor,
      accentColorBrightness,
286 287 288 289 290 291 292 293 294
      hashValues( // Too many values.
        indicatorColor,
        hintColor,
        hintOpacity,
        errorColor,
        text,
        primaryTextTheme,
        primaryIconTheme
      )
295
    );
296
  }
Hixie's avatar
Hixie committed
297

298
  String toString() => '$runtimeType($brightness $primaryColor etc...)';
299
}