theme_data.dart 72.1 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, hashList;
6

xster's avatar
xster committed
7
import 'package:flutter/cupertino.dart';
8
import 'package:flutter/foundation.dart';
9
import 'package:flutter/services.dart';
10
import 'package:flutter/widgets.dart';
11

12
import 'app_bar_theme.dart';
13
import 'banner_theme.dart';
14
import 'bottom_app_bar_theme.dart';
15
import 'bottom_sheet_theme.dart';
16
import 'button_bar_theme.dart';
17
import 'button_theme.dart';
18
import 'card_theme.dart';
19
import 'chip_theme.dart';
20
import 'color_scheme.dart';
21
import 'colors.dart';
22
import 'dialog_theme.dart';
23
import 'divider_theme.dart';
24
import 'floating_action_button_theme.dart';
25 26
import 'ink_splash.dart';
import 'ink_well.dart' show InteractiveInkFeatureFactory;
27
import 'input_decorator.dart';
28
import 'page_transitions_theme.dart';
29
import 'popup_menu_theme.dart';
30
import 'slider_theme.dart';
31
import 'snack_bar_theme.dart';
32
import 'tab_bar_theme.dart';
33
import 'text_theme.dart';
34
import 'toggle_buttons_theme.dart';
35
import 'tooltip_theme.dart';
Adam Barth's avatar
Adam Barth committed
36
import 'typography.dart';
37

38
export 'package:flutter/services.dart' show Brightness;
39

40 41 42 43 44
// 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:
45
const Color _kLightThemeHighlightColor = Color(0x66BCBCBC);
46 47 48 49

// 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:
50
const Color _kLightThemeSplashColor = Color(0x66C8C8C8);
51 52 53

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

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
/// Configures the tap target and layout size of certain Material widgets.
///
/// Changing the value in [ThemeData.materialTapTargetSize] will affect the
/// accessibility experience.
///
/// Some of the impacted widgets include:
///
///   * [FloatingActionButton], only the mini tap target size is increased.
///   * [MaterialButton]
///   * [OutlineButton]
///   * [FlatButton]
///   * [RaisedButton]
///   * [TimePicker]
///   * [SnackBar]
///   * [Chip]
///   * [RawChip]
///   * [InputChip]
///   * [ChoiceChip]
///   * [FilterChip]
///   * [ActionChip]
///   * [Radio]
///   * [Switch]
///   * [Checkbox]
enum MaterialTapTargetSize {
  /// Expands the minimum tap target size to 48px by 48px.
  ///
  /// This is the default value of [ThemeData.materialHitTestSize] and the
  /// recommended size to conform to Android accessibility scanner
  /// recommendations.
  padded,

  /// Shrinks the tap target size to the minimum provided by the Material
  /// specification.
  shrinkWrap,
}

93 94
/// Holds the color and typography values for a material design theme.
///
95
/// Use this class to configure a [Theme] or [MaterialApp] widget.
96 97
///
/// To obtain the current theme, use [Theme.of].
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
///
/// {@tool sample}
///
/// This sample creates a [Theme] widget that stores the `ThemeData`. The
/// `ThemeData` can be accessed by descendant Widgets that use the correct
/// `context`. This example uses the [Builder] widget to gain access to a
/// descendant `context` that contains the `ThemeData`.
///
/// The [Container] widget uses [Theme.of] to retrieve the [primaryColor] from
/// the `ThemeData` to draw an amber square.
///
/// ![](https://flutter.github.io/assets-for-api-docs/assets/material/theme_data.png)
///
/// ```dart
/// Theme(
///   data: ThemeData(primaryColor: Colors.amber),
///   child: Builder(
///     builder: (BuildContext context) {
///       return Container(
///         width: 100,
///         height: 100,
///         color: Theme.of(context).primaryColor,
///       );
///     },
///   ),
/// )
/// ```
/// {@end-tool}
///
/// In addition to using the [Theme] widget, you can provide `ThemeData` to a
/// [MaterialApp]. The `ThemeData` will be used throughout the app to style
/// material design widgets.
///
/// {@tool sample}
///
/// This sample creates a [MaterialApp] widget that stores `ThemeData` and
/// passes the `ThemeData` to descendant widgets. The [AppBar] widget uses the
/// [primaryColor] to create a blue background. The [Text] widget uses the
/// [TextTheme.body1] to create purple text. The [FloatingActionButton] widget
/// uses the [accentColor] to create a green background.
///
/// ![](https://flutter.github.io/assets-for-api-docs/assets/material/material_app_theme_data.png)
///
/// ```dart
/// MaterialApp(
///   theme: ThemeData(
///     primaryColor: Colors.blue,
///     accentColor: Colors.green,
///     textTheme: TextTheme(body1: TextStyle(color: Colors.purple)),
///   ),
///   home: Scaffold(
///     appBar: AppBar(
///       title: const Text('ThemeData Demo'),
///     ),
///     floatingActionButton: FloatingActionButton(
///       child: const Icon(Icons.add),
///       onPressed: () {},
///     ),
///     body: Center(
///       child: Text(
///         'Button pressed 0 times',
///       ),
///     ),
///   ),
/// )
/// ```
/// {@end-tool}
165
@immutable
166 167
class ThemeData extends Diagnosticable {
  /// Create a [ThemeData] given a set of preferred values.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
  ///
  /// Default values will be derived for arguments that are omitted.
  ///
  /// The most useful values to give are, in order of importance:
  ///
  ///  * The desired theme [brightness].
  ///
  ///  * The primary color palette (the [primarySwatch]), chosen from
  ///    one of the swatches defined by the material design spec. This
  ///    should be one of the maps from the [Colors] class that do not
  ///    have "accent" in their name.
  ///
  ///  * The [accentColor], sometimes called the secondary color, and,
  ///    if the accent color is specified, its brightness
  ///    ([accentColorBrightness]), so that the right contrasting text
  ///    color will be used over the accent color.
  ///
185 186 187 188 189 190 191 192 193 194 195 196 197 198
  /// Most of these parameters map to the [ThemeData] field with the same name,
  /// all of which are described in more detail on the fields themselves. The
  /// exceptions are:
  ///
  ///  * [primarySwatch] - used to configure default values for several fields,
  ///    including: [primaryColor], [primaryColorBrightness], [primaryColorLight],
  ///    [primaryColorDark], [toggleableActiveColor], [accentColor], [colorScheme],
  ///    [secondaryHeaderColor], [textSelectionColor], [backgroundColor], and
  ///    [buttonColor].
  ///
  ///  * [fontFamily] - sets the default fontFamily for any
  ///    [TextStyle.fontFamily] that isn't set directly in the [textTheme],
  ///    [primaryTextTheme], or [accentTextTheme].
  ///
199
  /// See <https://material.io/design/color/> for
200
  /// more discussion on how to pick the right colors.
201
  factory ThemeData({
202
    Brightness brightness,
203
    MaterialColor primarySwatch,
204
    Color primaryColor,
205
    Brightness primaryColorBrightness,
206 207
    Color primaryColorLight,
    Color primaryColorDark,
208
    Color accentColor,
209
    Brightness accentColorBrightness,
210
    Color canvasColor,
211
    Color scaffoldBackgroundColor,
212
    Color bottomAppBarColor,
213 214
    Color cardColor,
    Color dividerColor,
215 216
    Color focusColor,
    Color hoverColor,
217 218
    Color highlightColor,
    Color splashColor,
219
    InteractiveInkFeatureFactory splashFactory,
220 221
    Color selectedRowColor,
    Color unselectedWidgetColor,
222
    Color disabledColor,
223
    Color buttonColor,
224
    ButtonThemeData buttonTheme,
225
    ToggleButtonsThemeData toggleButtonsTheme,
226
    Color secondaryHeaderColor,
227
    Color textSelectionColor,
228
    Color cursorColor,
229
    Color textSelectionHandleColor,
230
    Color backgroundColor,
231
    Color dialogBackgroundColor,
232 233
    Color indicatorColor,
    Color hintColor,
234
    Color errorColor,
235
    Color toggleableActiveColor,
236
    String fontFamily,
237
    TextTheme textTheme,
238
    TextTheme primaryTextTheme,
239
    TextTheme accentTextTheme,
240
    InputDecorationTheme inputDecorationTheme,
Ian Hickson's avatar
Ian Hickson committed
241
    IconThemeData iconTheme,
242
    IconThemeData primaryIconTheme,
243
    IconThemeData accentIconTheme,
244
    SliderThemeData sliderTheme,
245
    TabBarTheme tabBarTheme,
246
    TooltipThemeData tooltipTheme,
247
    CardTheme cardTheme,
248
    ChipThemeData chipTheme,
249
    TargetPlatform platform,
250
    MaterialTapTargetSize materialTapTargetSize,
251
    bool applyElevationOverlayColor,
252
    PageTransitionsTheme pageTransitionsTheme,
253
    AppBarTheme appBarTheme,
254
    BottomAppBarTheme bottomAppBarTheme,
255
    ColorScheme colorScheme,
256
    DialogTheme dialogTheme,
257
    FloatingActionButtonThemeData floatingActionButtonTheme,
258
    Typography typography,
259
    CupertinoThemeData cupertinoOverrideTheme,
260
    SnackBarThemeData snackBarTheme,
261
    BottomSheetThemeData bottomSheetTheme,
262
    PopupMenuThemeData popupMenuTheme,
263
    MaterialBannerThemeData bannerTheme,
264
    DividerThemeData dividerTheme,
265
    ButtonBarThemeData buttonBarTheme,
266
  }) {
267 268
    brightness ??= Brightness.light;
    final bool isDark = brightness == Brightness.dark;
269
    primarySwatch ??= Colors.blue;
270
    primaryColor ??= isDark ? Colors.grey[900] : primarySwatch;
271
    primaryColorBrightness ??= estimateBrightnessForColor(primaryColor);
272 273
    primaryColorLight ??= isDark ? Colors.grey[500] : primarySwatch[100];
    primaryColorDark ??= isDark ? Colors.black : primarySwatch[700];
Ian Hickson's avatar
Ian Hickson committed
274
    final bool primaryIsDark = primaryColorBrightness == Brightness.dark;
275
    toggleableActiveColor ??= isDark ? Colors.tealAccent[200] : (accentColor ?? primarySwatch[600]);
Hans Muller's avatar
Hans Muller committed
276
    accentColor ??= isDark ? Colors.tealAccent[200] : primarySwatch[500];
277
    accentColorBrightness ??= estimateBrightnessForColor(accentColor);
278
    final bool accentIsDark = accentColorBrightness == Brightness.dark;
279
    canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50];
280
    scaffoldBackgroundColor ??= canvasColor;
281
    bottomAppBarColor ??= isDark ? Colors.grey[800] : Colors.white;
282 283
    cardColor ??= isDark ? Colors.grey[800] : Colors.white;
    dividerColor ??= isDark ? const Color(0x1FFFFFFF) : const Color(0x1F000000);
284 285 286 287 288 289 290 291 292 293 294 295 296

    // Create a ColorScheme that is backwards compatible as possible
    // with the existing default ThemeData color values.
    colorScheme ??= ColorScheme.fromSwatch(
      primarySwatch: primarySwatch,
      primaryColorDark: primaryColorDark,
      accentColor: accentColor,
      cardColor: cardColor,
      backgroundColor: backgroundColor,
      errorColor: errorColor,
      brightness: brightness,
    );

297
    splashFactory ??= InkSplash.splashFactory;
298 299
    selectedRowColor ??= Colors.grey[100];
    unselectedWidgetColor ??= isDark ? Colors.white70 : Colors.black54;
300 301
    // Spec doesn't specify a dark theme secondaryHeaderColor, this is a guess.
    secondaryHeaderColor ??= isDark ? Colors.grey[700] : primarySwatch[50];
302
    textSelectionColor ??= isDark ? accentColor : primarySwatch[200];
303
    // TODO(sandrasandeep): change to color provided by Material Design team
304
    cursorColor = cursorColor ?? const Color.fromRGBO(66, 133, 244, 1.0);
305
    textSelectionHandleColor ??= isDark ? Colors.tealAccent[400] : primarySwatch[300];
306
    backgroundColor ??= isDark ? Colors.grey[700] : primarySwatch[200];
307
    dialogBackgroundColor ??= isDark ? Colors.grey[800] : Colors.white;
308
    indicatorColor ??= accentColor == primaryColor ? Colors.white : accentColor;
309
    hintColor ??= isDark ? const Color(0x80FFFFFF) : const Color(0x8A000000);
310
    errorColor ??= Colors.red[700];
311
    inputDecorationTheme ??= const InputDecorationTheme();
312
    pageTransitionsTheme ??= const PageTransitionsTheme();
313 314
    primaryIconTheme ??= primaryIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
    accentIconTheme ??= accentIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
315
    iconTheme ??= isDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black87);
316
    platform ??= defaultTargetPlatform;
317
    typography ??= Typography(platform: platform);
318 319 320 321 322 323 324 325
    TextTheme defaultTextTheme = isDark ? typography.white : typography.black;
    TextTheme defaultPrimaryTextTheme = primaryIsDark ? typography.white : typography.black;
    TextTheme defaultAccentTextTheme = accentIsDark ? typography.white : typography.black;
    if (fontFamily != null) {
      defaultTextTheme = defaultTextTheme.apply(fontFamily: fontFamily);
      defaultPrimaryTextTheme = defaultPrimaryTextTheme.apply(fontFamily: fontFamily);
      defaultAccentTextTheme = defaultAccentTextTheme.apply(fontFamily: fontFamily);
    }
326 327 328
    textTheme = defaultTextTheme.merge(textTheme);
    primaryTextTheme = defaultPrimaryTextTheme.merge(primaryTextTheme);
    accentTextTheme = defaultAccentTextTheme.merge(accentTextTheme);
329
    materialTapTargetSize ??= MaterialTapTargetSize.padded;
330
    applyElevationOverlayColor ??= false;
331 332 333 334

    // Used as the default color (fill color) for RaisedButtons. Computing the
    // default for ButtonThemeData for the sake of backwards compatibility.
    buttonColor ??= isDark ? primarySwatch[600] : Colors.grey[300];
335 336
    focusColor ??= isDark ? Colors.white.withOpacity(0.12) : Colors.black.withOpacity(0.12);
    hoverColor ??= isDark ? Colors.white.withOpacity(0.04) : Colors.black.withOpacity(0.04);
337 338 339 340
    buttonTheme ??= ButtonThemeData(
      colorScheme: colorScheme,
      buttonColor: buttonColor,
      disabledColor: disabledColor,
341 342
      focusColor: focusColor,
      hoverColor: hoverColor,
343 344 345 346
      highlightColor: highlightColor,
      splashColor: splashColor,
      materialTapTargetSize: materialTapTargetSize,
    );
347
    toggleButtonsTheme ??= const ToggleButtonsThemeData();
348
    disabledColor ??= isDark ? Colors.white38 : Colors.black38;
349 350 351
    highlightColor ??= isDark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor;
    splashColor ??= isDark ? _kDarkThemeSplashColor : _kLightThemeSplashColor;

352
    sliderTheme ??= const SliderThemeData();
353
    tabBarTheme ??= const TabBarTheme();
354
    tooltipTheme ??= const TooltipThemeData();
355
    appBarTheme ??= const AppBarTheme();
356
    bottomAppBarTheme ??= const BottomAppBarTheme();
357
    cardTheme ??= const CardTheme();
358
    chipTheme ??= ChipThemeData.fromDefaults(
359 360 361 362
      secondaryColor: primaryColor,
      brightness: brightness,
      labelStyle: textTheme.body2,
    );
363
    dialogTheme ??= const DialogTheme();
364
    floatingActionButtonTheme ??= const FloatingActionButtonThemeData();
xster's avatar
xster committed
365
    cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
366
    snackBarTheme ??= const SnackBarThemeData();
367
    bottomSheetTheme ??= const BottomSheetThemeData();
368
    popupMenuTheme ??= const PopupMenuThemeData();
369
    bannerTheme ??= const MaterialBannerThemeData();
370
    dividerTheme ??= const DividerThemeData();
371
    buttonBarTheme ??= const ButtonBarThemeData();
372

373
    return ThemeData.raw(
374 375 376
      brightness: brightness,
      primaryColor: primaryColor,
      primaryColorBrightness: primaryColorBrightness,
377 378
      primaryColorLight: primaryColorLight,
      primaryColorDark: primaryColorDark,
379 380
      accentColor: accentColor,
      accentColorBrightness: accentColorBrightness,
381
      canvasColor: canvasColor,
382
      scaffoldBackgroundColor: scaffoldBackgroundColor,
383
      bottomAppBarColor: bottomAppBarColor,
384 385
      cardColor: cardColor,
      dividerColor: dividerColor,
386 387
      focusColor: focusColor,
      hoverColor: hoverColor,
388 389
      highlightColor: highlightColor,
      splashColor: splashColor,
390
      splashFactory: splashFactory,
391 392
      selectedRowColor: selectedRowColor,
      unselectedWidgetColor: unselectedWidgetColor,
393
      disabledColor: disabledColor,
394
      buttonTheme: buttonTheme,
395
      buttonColor: buttonColor,
396
      toggleButtonsTheme: toggleButtonsTheme,
397
      toggleableActiveColor: toggleableActiveColor,
398
      secondaryHeaderColor: secondaryHeaderColor,
399
      textSelectionColor: textSelectionColor,
400
      cursorColor: cursorColor,
401
      textSelectionHandleColor: textSelectionHandleColor,
402
      backgroundColor: backgroundColor,
403
      dialogBackgroundColor: dialogBackgroundColor,
404 405
      indicatorColor: indicatorColor,
      hintColor: hintColor,
406
      errorColor: errorColor,
407
      textTheme: textTheme,
408
      primaryTextTheme: primaryTextTheme,
409
      accentTextTheme: accentTextTheme,
410
      inputDecorationTheme: inputDecorationTheme,
Ian Hickson's avatar
Ian Hickson committed
411
      iconTheme: iconTheme,
412
      primaryIconTheme: primaryIconTheme,
413
      accentIconTheme: accentIconTheme,
414
      sliderTheme: sliderTheme,
415
      tabBarTheme: tabBarTheme,
416
      tooltipTheme: tooltipTheme,
417
      cardTheme: cardTheme,
418
      chipTheme: chipTheme,
419
      platform: platform,
420
      materialTapTargetSize: materialTapTargetSize,
421
      applyElevationOverlayColor: applyElevationOverlayColor,
422
      pageTransitionsTheme: pageTransitionsTheme,
423
      appBarTheme: appBarTheme,
424
      bottomAppBarTheme: bottomAppBarTheme,
425
      colorScheme: colorScheme,
426
      dialogTheme: dialogTheme,
427
      floatingActionButtonTheme: floatingActionButtonTheme,
428
      typography: typography,
xster's avatar
xster committed
429
      cupertinoOverrideTheme: cupertinoOverrideTheme,
430
      snackBarTheme: snackBarTheme,
431
      bottomSheetTheme: bottomSheetTheme,
432
      popupMenuTheme: popupMenuTheme,
433
      bannerTheme: bannerTheme,
434
      dividerTheme: dividerTheme,
435
      buttonBarTheme: buttonBarTheme,
436
    );
437 438
  }

xster's avatar
xster committed
439 440 441
  /// Create a [ThemeData] given a set of exact values. All the values must be
  /// specified. They all must also be non-null except for
  /// [cupertinoOverrideTheme].
442 443 444 445
  ///
  /// This will rarely be used directly. It is used by [lerp] to
  /// create intermediate themes based on two themes created with the
  /// [new ThemeData] constructor.
446
  const ThemeData.raw({
447 448 449
    // Warning: make sure these properties are in the exact same order as in
    // operator == and in the hashValues method and in the order of fields
    // in this class, and in the lerp() method.
450 451 452
    @required this.brightness,
    @required this.primaryColor,
    @required this.primaryColorBrightness,
453 454
    @required this.primaryColorLight,
    @required this.primaryColorDark,
455
    @required this.canvasColor,
456 457 458
    @required this.accentColor,
    @required this.accentColorBrightness,
    @required this.scaffoldBackgroundColor,
459
    @required this.bottomAppBarColor,
460 461
    @required this.cardColor,
    @required this.dividerColor,
462 463
    @required this.focusColor,
    @required this.hoverColor,
464 465
    @required this.highlightColor,
    @required this.splashColor,
466
    @required this.splashFactory,
467 468 469
    @required this.selectedRowColor,
    @required this.unselectedWidgetColor,
    @required this.disabledColor,
470
    @required this.buttonTheme,
471
    @required this.buttonColor,
472
    @required this.toggleButtonsTheme,
473 474
    @required this.secondaryHeaderColor,
    @required this.textSelectionColor,
475
    @required this.cursorColor,
476 477 478 479 480 481
    @required this.textSelectionHandleColor,
    @required this.backgroundColor,
    @required this.dialogBackgroundColor,
    @required this.indicatorColor,
    @required this.hintColor,
    @required this.errorColor,
482
    @required this.toggleableActiveColor,
483 484 485
    @required this.textTheme,
    @required this.primaryTextTheme,
    @required this.accentTextTheme,
486
    @required this.inputDecorationTheme,
487 488 489
    @required this.iconTheme,
    @required this.primaryIconTheme,
    @required this.accentIconTheme,
490
    @required this.sliderTheme,
491
    @required this.tabBarTheme,
492
    @required this.tooltipTheme,
493
    @required this.cardTheme,
494
    @required this.chipTheme,
495
    @required this.platform,
496
    @required this.materialTapTargetSize,
497
    @required this.applyElevationOverlayColor,
498
    @required this.pageTransitionsTheme,
499
    @required this.appBarTheme,
500
    @required this.bottomAppBarTheme,
501
    @required this.colorScheme,
502
    @required this.dialogTheme,
503
    @required this.floatingActionButtonTheme,
504
    @required this.typography,
xster's avatar
xster committed
505
    @required this.cupertinoOverrideTheme,
506
    @required this.snackBarTheme,
507
    @required this.bottomSheetTheme,
508
    @required this.popupMenuTheme,
509
    @required this.bannerTheme,
510
    @required this.dividerTheme,
511
    @required this.buttonBarTheme,
512 513 514
  }) : assert(brightness != null),
       assert(primaryColor != null),
       assert(primaryColorBrightness != null),
515 516
       assert(primaryColorLight != null),
       assert(primaryColorDark != null),
517 518 519 520
       assert(accentColor != null),
       assert(accentColorBrightness != null),
       assert(canvasColor != null),
       assert(scaffoldBackgroundColor != null),
521
       assert(bottomAppBarColor != null),
522 523
       assert(cardColor != null),
       assert(dividerColor != null),
524 525
       assert(focusColor != null),
       assert(hoverColor != null),
526 527
       assert(highlightColor != null),
       assert(splashColor != null),
528
       assert(splashFactory != null),
529 530 531
       assert(selectedRowColor != null),
       assert(unselectedWidgetColor != null),
       assert(disabledColor != null),
532
       assert(toggleableActiveColor != null),
533
       assert(buttonTheme != null),
534
       assert(toggleButtonsTheme != null),
535 536
       assert(secondaryHeaderColor != null),
       assert(textSelectionColor != null),
537
       assert(cursorColor != null),
538 539 540 541 542 543 544 545 546
       assert(textSelectionHandleColor != null),
       assert(backgroundColor != null),
       assert(dialogBackgroundColor != null),
       assert(indicatorColor != null),
       assert(hintColor != null),
       assert(errorColor != null),
       assert(textTheme != null),
       assert(primaryTextTheme != null),
       assert(accentTextTheme != null),
547
       assert(inputDecorationTheme != null),
548 549 550
       assert(iconTheme != null),
       assert(primaryIconTheme != null),
       assert(accentIconTheme != null),
551
       assert(sliderTheme != null),
552
       assert(tabBarTheme != null),
553
       assert(tooltipTheme != null),
554
       assert(cardTheme != null),
555
       assert(chipTheme != null),
556
       assert(platform != null),
557
       assert(materialTapTargetSize != null),
558
       assert(pageTransitionsTheme != null),
559
       assert(appBarTheme != null),
560
       assert(bottomAppBarTheme != null),
561
       assert(colorScheme != null),
562
       assert(dialogTheme != null),
563
       assert(floatingActionButtonTheme != null),
564
       assert(typography != null),
565
       assert(snackBarTheme != null),
566
       assert(bottomSheetTheme != null),
567
       assert(popupMenuTheme != null),
568
       assert(bannerTheme != null),
569 570
       assert(dividerTheme != null),
       assert(buttonBarTheme != null);
571

572 573 574 575 576 577 578
  /// 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
579 580
  /// the Material dark theme method for indicating elevation by applying
  /// a semi-transparent onSurface color on top of the surface color.
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
  ///
  /// 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 <https://material.io/design/color/> 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,
620
      bottomAppBarColor: colorScheme.surface,
621 622 623 624 625 626 627 628 629 630 631
      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,
    );
  }
Ian Hickson's avatar
Ian Hickson committed
632

633
  /// A default light blue theme.
634 635 636
  ///
  /// This theme does not contain text geometry. Instead, it is expected that
  /// this theme is localized using text geometry using [ThemeData.localize].
637
  factory ThemeData.light() => ThemeData(brightness: Brightness.light);
638 639

  /// A default dark theme with a teal accent color.
640 641 642
  ///
  /// This theme does not contain text geometry. Instead, it is expected that
  /// this theme is localized using text geometry using [ThemeData.localize].
643
  factory ThemeData.dark() => ThemeData(brightness: Brightness.dark);
644

645
  /// The default color theme. Same as [new ThemeData.light].
646 647
  ///
  /// This is used by [Theme.of] when no theme has been specified.
648 649 650 651 652 653
  ///
  /// This theme does not contain text geometry. Instead, it is expected that
  /// this theme is localized using text geometry using [ThemeData.localize].
  ///
  /// Most applications would use [Theme.of], which provides correct localized
  /// text geometry.
654
  factory ThemeData.fallback() => ThemeData.light();
655

656 657 658 659
  // 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.

660 661 662
  /// 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.
663
  ///
664 665
  /// When the [Brightness] is dark, the canvas, card, and primary colors are
  /// all dark. When the [Brightness] is light, the canvas and card colors
666 667
  /// are bright, and the primary color's darkness varies as described by
  /// primaryColorBrightness. The primaryColor does not contrast well with the
668
  /// card and canvas colors when the brightness is dark; when the brightness is
669
  /// dark, use Colors.white or the accentColor for a contrasting color.
670
  final Brightness brightness;
671

672
  /// The background color for major parts of the app (toolbars, tab bars, etc)
673 674 675 676 677
  ///
  /// The theme's [colorScheme] property contains [ColorScheme.primary], as
  /// well as a color that contrasts well with the primary color called
  /// [ColorScheme.onPrimary]. It might be simpler to just configure an app's
  /// visuals in terms of the theme's [colorScheme].
678
  final Color primaryColor;
679

680
  /// The brightness of the [primaryColor]. Used to determine the color of text and
681
  /// icons placed on top of the primary color (e.g. toolbar text).
682
  final Brightness primaryColorBrightness;
683

684 685 686 687 688 689
  /// A lighter version of the [primaryColor].
  final Color primaryColorLight;

  /// A darker version of the [primaryColor].
  final Color primaryColorDark;

690 691 692
  /// The default color of [MaterialType.canvas] [Material].
  final Color canvasColor;

693
  /// The foreground color for widgets (knobs, text, overscroll edge effect, etc).
694 695 696 697 698 699 700
  ///
  /// Accent color is also known as the secondary color.
  ///
  /// The theme's [colorScheme] property contains [ColorScheme.secondary], as
  /// well as a color that contrasts well with the secondary color called
  /// [ColorScheme.onSecondary]. It might be simpler to just configure an app's
  /// visuals in terms of the theme's [colorScheme].
701
  final Color accentColor;
702

703
  /// The brightness of the [accentColor]. Used to determine the color of text
704 705
  /// and icons placed on top of the accent color (e.g. the icons on a floating
  /// action button).
706
  final Brightness accentColorBrightness;
707

708 709 710 711
  /// The default color of the [Material] that underlies the [Scaffold]. The
  /// background color for a typical material app or a page within the app.
  final Color scaffoldBackgroundColor;

712 713
  /// The default color of the [BottomAppBar].
  ///
Josh Soref's avatar
Josh Soref committed
714
  /// This can be overridden by specifying [BottomAppBar.color].
715 716
  final Color bottomAppBarColor;

717
  /// The color of [Material] when it is used as a [Card].
718
  final Color cardColor;
719 720

  /// The color of [Divider]s and [PopupMenuDivider]s, also used
721
  /// between [ListTile]s, between rows in [DataTable]s, and so forth.
722 723 724
  ///
  /// To create an appropriate [BorderSide] that uses this color, consider
  /// [Divider.createBorderSide].
725
  final Color dividerColor;
726

727 728 729 730 731 732 733
  /// The focus color used indicate that a component has the input focus.
  final Color focusColor;

  /// The hover color used to indicate when a pointer is hovering over a
  /// component.
  final Color hoverColor;

734 735
  /// The highlight color used during ink splash animations or to
  /// indicate an item in a menu is selected.
736
  final Color highlightColor;
737 738

  /// The color of ink splashes. See [InkWell].
739
  final Color splashColor;
740

741 742 743 744 745 746 747
  /// Defines the appearance of ink splashes produces by [InkWell]
  /// and [InkResponse].
  ///
  /// See also:
  ///
  ///  * [InkSplash.splashFactory], which defines the default splash.
  ///  * [InkRipple.splashFactory], which defines a splash that spreads out
748
  ///    more aggressively than the default.
749 750
  final InteractiveInkFeatureFactory splashFactory;

751 752 753 754 755 756 757 758 759 760 761
  /// The color used to highlight selected rows.
  final Color selectedRowColor;

  /// The color used for widgets in their inactive (but enabled)
  /// state. For example, an unchecked checkbox. Usually contrasted
  /// with the [accentColor]. See also [disabledColor].
  final Color unselectedWidgetColor;

  /// The color used for widgets that are inoperative, regardless of
  /// their state. For example, a disabled checkbox (which may be
  /// checked or unchecked).
762
  final Color disabledColor;
763

764 765 766 767
  /// Defines the default configuration of button widgets, like [RaisedButton]
  /// and [FlatButton].
  final ButtonThemeData buttonTheme;

768 769 770
  /// Defines the default configuration of [ToggleButtons] widgets.
  final ToggleButtonsThemeData toggleButtonsTheme;

771 772 773
  /// The default fill color of the [Material] used in [RaisedButton]s.
  final Color buttonColor;

774 775
  /// The color of the header of a [PaginatedDataTable] when there are selected rows.
  // According to the spec for data tables:
776
  // https://material.io/archive/guidelines/components/data-tables.html#data-tables-tables-within-cards
777 778 779
  // ...this should be the "50-value of secondary app color".
  final Color secondaryHeaderColor;

780
  /// The color of text selections in text fields, such as [TextField].
781 782
  final Color textSelectionColor;

783 784 785
  /// The color of cursors in Material-style text fields, such as [TextField].
  final Color cursorColor;

786
  /// The color of the handles used to adjust what part of the text is currently selected.
787 788
  final Color textSelectionHandleColor;

789 790
  /// A color that contrasts with the [primaryColor], e.g. used as the
  /// remaining part of a progress bar.
791 792
  final Color backgroundColor;

793 794
  /// The background color of [Dialog] elements.
  final Color dialogBackgroundColor;
795

796
  /// The color of the selected tab indicator in a tab bar.
797 798
  final Color indicatorColor;

799
  /// The color to use for hint text or placeholder text, e.g. in
800
  /// [TextField] fields.
801 802
  final Color hintColor;

803
  /// The color to use for input validation errors, e.g. in [TextField] fields.
804 805
  final Color errorColor;

806 807 808 809
  /// The color used to highlight the active states of toggleable widgets like
  /// [Switch], [Radio], and [Checkbox].
  final Color toggleableActiveColor;

810
  /// Text with a color that contrasts with the card and canvas colors.
811
  final TextTheme textTheme;
812 813 814 815

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

816 817 818
  /// A text theme that contrasts with the accent color.
  final TextTheme accentTextTheme;

819 820 821 822 823 824
  /// The default [InputDecoration] values for [InputDecorator], [TextField],
  /// and [TextFormField] are based on this theme.
  ///
  /// See [InputDecoration.applyDefaults].
  final InputDecorationTheme inputDecorationTheme;

Ian Hickson's avatar
Ian Hickson committed
825 826 827
  /// An icon theme that contrasts with the card and canvas colors.
  final IconThemeData iconTheme;

828
  /// An icon theme that contrasts with the primary color.
829 830
  final IconThemeData primaryIconTheme;

831 832 833
  /// An icon theme that contrasts with the accent color.
  final IconThemeData accentIconTheme;

834 835 836 837 838
  /// The colors and shapes used to render [Slider].
  ///
  /// This is the value returned from [SliderTheme.of].
  final SliderThemeData sliderTheme;

839 840 841
  /// A theme for customizing the size, shape, and color of the tab bar indicator.
  final TabBarTheme tabBarTheme;

842 843 844 845 846
  /// A theme for customizing the visual properties of [Tooltip]s.
  ///
  /// This is the value returned from [TooltipTheme.of].
  final TooltipThemeData tooltipTheme;

847 848 849 850 851
  /// The colors and styles used to render [Card].
  ///
  /// This is the value returned from [CardTheme.of].
  final CardTheme cardTheme;

852
  /// The colors and styles used to render [Chip]s.
853 854 855 856
  ///
  /// This is the value returned from [ChipTheme.of].
  final ChipThemeData chipTheme;

857 858
  /// The platform the material widgets should adapt to target.
  ///
859 860 861 862 863 864 865 866 867 868 869
  /// Defaults to the current platform, as exposed by [defaultTargetPlatform].
  /// This should be used in order to style UI elements according to platform
  /// conventions.
  ///
  /// Widgets from the material library should use this getter (via [Theme.of])
  /// to determine the current platform for the purpose of emulating the
  /// platform behavior (e.g. scrolling or haptic effects). Widgets and render
  /// objects at lower layers that try to emulate the underlying platform
  /// platform can depend on [defaultTargetPlatform] directly, or may require
  /// that the target platform be provided as an argument. The
  /// [dart.io.Platform] object should only be used directly when it's critical
870 871
  /// to actually know the current platform, without any overrides possible (for
  /// example, when a system API is about to be called).
872 873 874 875 876 877 878
  ///
  /// In a test environment, the platform returned is [TargetPlatform.android]
  /// regardless of the host platform. (Android was chosen because the tests
  /// were originally written assuming Android-like behavior, and we added
  /// platform adaptations for iOS later). Tests can check iOS behavior by
  /// setting the [platform] of the [Theme] explicitly to [TargetPlatform.iOS],
  /// or by setting [debugDefaultTargetPlatformOverride].
879 880
  final TargetPlatform platform;

881 882 883
  /// Configures the hit test size of certain Material widgets.
  final MaterialTapTargetSize materialTapTargetSize;

884
  /// Apply a semi-transparent overlay color on Material surfaces to indicate
885 886 887 888
  /// elevation for dark themes.
  ///
  /// Material drop shadows can be difficult to see in a dark theme, so the
  /// elevation of a surface should be portrayed with an "overlay" in addition
889
  /// to the shadow. As the elevation of the component increases, the
890 891 892
  /// overlay increases in opacity. [applyElevationOverlayColor] turns the
  /// application of this overlay on or off.
  ///
893 894 895 896
  /// If [true] a semi-transparent version of [colorScheme.onSurface] will be
  /// applied on top of the color of [Material] widgets when their [Material.color]
  /// is [colorScheme.surface]. The level of transparency is based on
  /// [Material.elevation] as per the Material Dark theme specification.
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
  ///
  /// If [false] the surface color will be used unmodified.
  ///
  /// Defaults to [false].
  ///
  /// Note: this setting is here to maintain backwards compatibility with
  /// apps that were built before the Material Dark theme specification
  /// was published. New apps should set this to [true] for any themes
  /// where [brightness] is [Brightness.dark].
  ///
  /// See also:
  ///
  ///  * [Material.elevation], which effects how transparent the white overlay is.
  ///  * [Material.color], the white color overlay will only be applied of the
  ///    material's color is [colorScheme.surface].
  ///  * <https://material.io/design/color/dark-theme.html>, which specifies how
  ///    the overlay should be applied.
  final bool applyElevationOverlayColor;

916 917 918 919 920 921 922
  /// Default [MaterialPageRoute] transitions per [TargetPlatform].
  ///
  /// [MaterialPageRoute.buildTransitions] delegates to a [PageTransitionsBuilder]
  /// whose [PageTransitionsBuilder.platform] matches [platform]. If a matching
  /// builder is not found, a builder whose platform is null is used.
  final PageTransitionsTheme pageTransitionsTheme;

923 924 925 926
  /// A theme for customizing the color, elevation, brightness, iconTheme and
  /// textTheme of [AppBar]s.
  final AppBarTheme appBarTheme;

927 928 929
  /// A theme for customizing the shape, elevation, and color of a [BottomAppBar].
  final BottomAppBarTheme bottomAppBarTheme;

930 931 932 933 934 935 936 937 938 939
  /// A set of thirteen colors that can be used to configure the
  /// color properties of most components.
  ///
  /// This property was added much later than the theme's set of highly
  /// specific colors, like [cardColor], [buttonColor], [canvasColor] etc.
  /// New components can be defined exclusively in terms of [colorScheme].
  /// Existing components will gradually migrate to it, to the extent
  /// that is possible without significant backwards compatibility breaks.
  final ColorScheme colorScheme;

940 941 942
  /// A theme for customizing colors, shape, elevation, and behavior of a [SnackBar].
  final SnackBarThemeData snackBarTheme;

943 944 945
  /// A theme for customizing the shape of a dialog.
  final DialogTheme dialogTheme;

946 947 948 949
  /// A theme for customizing the shape, elevation, and color of a
  /// [FloatingActionButton].
  final FloatingActionButtonThemeData floatingActionButtonTheme;

950 951 952 953
  /// The color and geometry [TextTheme] values used to configure [textTheme],
  /// [primaryTextTheme], and [accentTextTheme].
  final Typography typography;

xster's avatar
xster committed
954 955 956 957 958 959 960 961 962 963 964 965
  /// Components of the [CupertinoThemeData] to override from the Material
  /// [ThemeData] adaptation.
  ///
  /// By default, [cupertinoOverrideTheme] is null and Cupertino widgets
  /// descendant to the Material [Theme] will adhere to a [CupertinoTheme]
  /// derived from the Material [ThemeData]. e.g. [ThemeData]'s [ColorTheme]
  /// will also inform the [CupertinoThemeData]'s `primaryColor` etc.
  ///
  /// This cascading effect for individual attributes of the [CupertinoThemeData]
  /// can be overridden using attributes of this [cupertinoOverrideTheme].
  final CupertinoThemeData cupertinoOverrideTheme;

966 967 968
  /// A theme for customizing the color, elevation, and shape of a bottom sheet.
  final BottomSheetThemeData bottomSheetTheme;

969 970 971 972
  /// A theme for customizing the color, shape, elevation, and text style of
  /// popup menus.
  final PopupMenuThemeData popupMenuTheme;

973 974 975
  /// A theme for customizing the color and text style of a [MaterialBanner].
  final MaterialBannerThemeData bannerTheme;

976 977 978 979
  /// A theme for customizing the color, thickness, and indents of [Divider]s,
  /// [VerticalDivider]s, etc.
  final DividerThemeData dividerTheme;

980 981 982
  /// A theme for customizing the appearance and layout of [ButtonBar] widgets.
  final ButtonBarThemeData buttonBarTheme;

983 984 985 986 987
  /// Creates a copy of this theme but with the given fields replaced with the new values.
  ThemeData copyWith({
    Brightness brightness,
    Color primaryColor,
    Brightness primaryColorBrightness,
988 989
    Color primaryColorLight,
    Color primaryColorDark,
990 991 992
    Color accentColor,
    Brightness accentColorBrightness,
    Color canvasColor,
993
    Color scaffoldBackgroundColor,
994
    Color bottomAppBarColor,
995 996
    Color cardColor,
    Color dividerColor,
997 998
    Color focusColor,
    Color hoverColor,
999 1000
    Color highlightColor,
    Color splashColor,
1001
    InteractiveInkFeatureFactory splashFactory,
1002 1003 1004
    Color selectedRowColor,
    Color unselectedWidgetColor,
    Color disabledColor,
1005
    ButtonThemeData buttonTheme,
1006
    ToggleButtonsThemeData toggleButtonsTheme,
1007
    Color buttonColor,
1008 1009
    Color secondaryHeaderColor,
    Color textSelectionColor,
1010
    Color cursorColor,
1011 1012
    Color textSelectionHandleColor,
    Color backgroundColor,
1013
    Color dialogBackgroundColor,
1014 1015 1016
    Color indicatorColor,
    Color hintColor,
    Color errorColor,
1017
    Color toggleableActiveColor,
1018 1019
    TextTheme textTheme,
    TextTheme primaryTextTheme,
1020
    TextTheme accentTextTheme,
1021
    InputDecorationTheme inputDecorationTheme,
1022 1023
    IconThemeData iconTheme,
    IconThemeData primaryIconTheme,
1024
    IconThemeData accentIconTheme,
1025
    SliderThemeData sliderTheme,
1026
    TabBarTheme tabBarTheme,
1027
    TooltipThemeData tooltipTheme,
1028
    CardTheme cardTheme,
1029
    ChipThemeData chipTheme,
1030
    TargetPlatform platform,
1031
    MaterialTapTargetSize materialTapTargetSize,
1032
    bool applyElevationOverlayColor,
1033
    PageTransitionsTheme pageTransitionsTheme,
1034
    AppBarTheme appBarTheme,
1035
    BottomAppBarTheme bottomAppBarTheme,
1036
    ColorScheme colorScheme,
1037
    DialogTheme dialogTheme,
1038
    FloatingActionButtonThemeData floatingActionButtonTheme,
1039
    Typography typography,
xster's avatar
xster committed
1040
    CupertinoThemeData cupertinoOverrideTheme,
1041
    SnackBarThemeData snackBarTheme,
1042
    BottomSheetThemeData bottomSheetTheme,
1043
    PopupMenuThemeData popupMenuTheme,
1044
    MaterialBannerThemeData bannerTheme,
1045
    DividerThemeData dividerTheme,
1046
    ButtonBarThemeData buttonBarTheme,
1047
  }) {
xster's avatar
xster committed
1048
    cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
1049
    return ThemeData.raw(
1050 1051 1052
      brightness: brightness ?? this.brightness,
      primaryColor: primaryColor ?? this.primaryColor,
      primaryColorBrightness: primaryColorBrightness ?? this.primaryColorBrightness,
1053 1054
      primaryColorLight: primaryColorLight ?? this.primaryColorLight,
      primaryColorDark: primaryColorDark ?? this.primaryColorDark,
1055 1056 1057 1058
      accentColor: accentColor ?? this.accentColor,
      accentColorBrightness: accentColorBrightness ?? this.accentColorBrightness,
      canvasColor: canvasColor ?? this.canvasColor,
      scaffoldBackgroundColor: scaffoldBackgroundColor ?? this.scaffoldBackgroundColor,
1059
      bottomAppBarColor: bottomAppBarColor ?? this.bottomAppBarColor,
1060 1061
      cardColor: cardColor ?? this.cardColor,
      dividerColor: dividerColor ?? this.dividerColor,
1062 1063
      focusColor: focusColor ?? this.focusColor,
      hoverColor: hoverColor ?? this.hoverColor,
1064 1065
      highlightColor: highlightColor ?? this.highlightColor,
      splashColor: splashColor ?? this.splashColor,
1066
      splashFactory: splashFactory ?? this.splashFactory,
1067 1068 1069 1070
      selectedRowColor: selectedRowColor ?? this.selectedRowColor,
      unselectedWidgetColor: unselectedWidgetColor ?? this.unselectedWidgetColor,
      disabledColor: disabledColor ?? this.disabledColor,
      buttonColor: buttonColor ?? this.buttonColor,
1071
      buttonTheme: buttonTheme ?? this.buttonTheme,
1072
      toggleButtonsTheme: toggleButtonsTheme ?? this.toggleButtonsTheme,
1073 1074
      secondaryHeaderColor: secondaryHeaderColor ?? this.secondaryHeaderColor,
      textSelectionColor: textSelectionColor ?? this.textSelectionColor,
1075
      cursorColor: cursorColor ?? this.cursorColor,
1076 1077 1078 1079 1080 1081
      textSelectionHandleColor: textSelectionHandleColor ?? this.textSelectionHandleColor,
      backgroundColor: backgroundColor ?? this.backgroundColor,
      dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor,
      indicatorColor: indicatorColor ?? this.indicatorColor,
      hintColor: hintColor ?? this.hintColor,
      errorColor: errorColor ?? this.errorColor,
1082
      toggleableActiveColor: toggleableActiveColor ?? this.toggleableActiveColor,
1083 1084 1085
      textTheme: textTheme ?? this.textTheme,
      primaryTextTheme: primaryTextTheme ?? this.primaryTextTheme,
      accentTextTheme: accentTextTheme ?? this.accentTextTheme,
1086
      inputDecorationTheme: inputDecorationTheme ?? this.inputDecorationTheme,
1087 1088 1089
      iconTheme: iconTheme ?? this.iconTheme,
      primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme,
      accentIconTheme: accentIconTheme ?? this.accentIconTheme,
1090
      sliderTheme: sliderTheme ?? this.sliderTheme,
1091
      tabBarTheme: tabBarTheme ?? this.tabBarTheme,
1092
      tooltipTheme: tooltipTheme ?? this.tooltipTheme,
1093
      cardTheme: cardTheme ?? this.cardTheme,
1094
      chipTheme: chipTheme ?? this.chipTheme,
1095
      platform: platform ?? this.platform,
1096
      materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
1097
      applyElevationOverlayColor: applyElevationOverlayColor ?? this.applyElevationOverlayColor,
1098
      pageTransitionsTheme: pageTransitionsTheme ?? this.pageTransitionsTheme,
1099
      appBarTheme: appBarTheme ?? this.appBarTheme,
1100
      bottomAppBarTheme: bottomAppBarTheme ?? this.bottomAppBarTheme,
1101
      colorScheme: colorScheme ?? this.colorScheme,
1102
      dialogTheme: dialogTheme ?? this.dialogTheme,
1103
      floatingActionButtonTheme: floatingActionButtonTheme ?? this.floatingActionButtonTheme,
1104
      typography: typography ?? this.typography,
xster's avatar
xster committed
1105
      cupertinoOverrideTheme: cupertinoOverrideTheme ?? this.cupertinoOverrideTheme,
1106
      snackBarTheme: snackBarTheme ?? this.snackBarTheme,
1107
      bottomSheetTheme: bottomSheetTheme ?? this.bottomSheetTheme,
1108
      popupMenuTheme: popupMenuTheme ?? this.popupMenuTheme,
1109
      bannerTheme: bannerTheme ?? this.bannerTheme,
1110
      dividerTheme: dividerTheme ?? this.dividerTheme,
1111
      buttonBarTheme: buttonBarTheme ?? this.buttonBarTheme,
1112 1113 1114
    );
  }

1115 1116 1117 1118 1119 1120 1121
  // The number 5 was chosen without any real science or research behind it. It
  // just seemed like a number that's not too big (we should be able to fit 5
  // copies of ThemeData in memory comfortably) and not too small (most apps
  // shouldn't have more than 5 theme/localization pairs).
  static const int _localizedThemeDataCacheSize = 5;

  /// Caches localized themes to speed up the [localize] method.
1122
  static final _FifoCache<_IdentityThemeDataCacheKey, ThemeData> _localizedThemeDataCache =
1123
      _FifoCache<_IdentityThemeDataCacheKey, ThemeData>(_localizedThemeDataCacheSize);
1124

1125 1126
  /// Returns a new theme built by merging the text geometry provided by the
  /// [localTextGeometry] theme with the [baseTheme].
1127
  ///
1128 1129 1130 1131
  /// For those text styles in the [baseTheme] whose [TextStyle.inherit] is set
  /// to true, the returned theme's text styles inherit the geometric properties
  /// of [localTextGeometry]. The resulting text styles' [TextStyle.inherit] is
  /// set to those provided by [localTextGeometry].
1132
  static ThemeData localize(ThemeData baseTheme, TextTheme localTextGeometry) {
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
    // WARNING: this method memoizes the result in a cache based on the
    // previously seen baseTheme and localTextGeometry. Memoization is safe
    // because all inputs and outputs of this function are deeply immutable, and
    // the computations are referentially transparent. It only short-circuits
    // the computation if the new inputs are identical() to the previous ones.
    // It does not use the == operator, which performs a costly deep comparison.
    //
    // When changing this method, make sure the memoization logic is correct.
    // Remember:
    //
    // There are only two hard things in Computer Science: cache invalidation
    // and naming things. -- Phil Karlton
1145 1146
    assert(baseTheme != null);
    assert(localTextGeometry != null);
1147 1148

    return _localizedThemeDataCache.putIfAbsent(
1149
      _IdentityThemeDataCacheKey(baseTheme, localTextGeometry),
1150 1151 1152 1153 1154 1155 1156 1157
      () {
        return baseTheme.copyWith(
          primaryTextTheme: localTextGeometry.merge(baseTheme.primaryTextTheme),
          accentTextTheme: localTextGeometry.merge(baseTheme.accentTextTheme),
          textTheme: localTextGeometry.merge(baseTheme.textTheme),
        );
      },
    );
1158 1159
  }

1160 1161 1162 1163 1164 1165
  /// Determines whether the given [Color] is [Brightness.light] or
  /// [Brightness.dark].
  ///
  /// This compares the luminosity of the given color to a threshold value that
  /// matches the material design specification.
  static Brightness estimateBrightnessForColor(Color color) {
1166
    final double relativeLuminance = color.computeLuminance();
1167 1168 1169 1170 1171 1172

    // See <https://www.w3.org/TR/WCAG20/#contrast-ratiodef>
    // The spec says to use kThreshold=0.0525, but Material Design appears to bias
    // more towards using light text than WCAG20 recommends. Material Design spec
    // doesn't say what value to use, but 0.15 seemed close to what the Material
    // Design spec shows for its color palette on
1173
    // <https://material.io/go/design-theming#color-color-palette>.
1174
    const double kThreshold = 0.15;
1175
    if ((relativeLuminance + 0.05) * (relativeLuminance + 0.05) > kThreshold)
1176 1177 1178 1179
      return Brightness.light;
    return Brightness.dark;
  }

1180
  /// Linearly interpolate between two themes.
1181
  ///
1182
  /// The arguments must not be null.
1183
  ///
1184
  /// {@macro dart.ui.shadow.lerp}
1185 1186 1187 1188
  static ThemeData lerp(ThemeData a, ThemeData b, double t) {
    assert(a != null);
    assert(b != null);
    assert(t != null);
1189 1190 1191
    // 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.
1192
    return ThemeData.raw(
1193 1194 1195
      brightness: t < 0.5 ? a.brightness : b.brightness,
      primaryColor: Color.lerp(a.primaryColor, b.primaryColor, t),
      primaryColorBrightness: t < 0.5 ? a.primaryColorBrightness : b.primaryColorBrightness,
1196 1197
      primaryColorLight: Color.lerp(a.primaryColorLight, b.primaryColorLight, t),
      primaryColorDark: Color.lerp(a.primaryColorDark, b.primaryColorDark, t),
1198
      canvasColor: Color.lerp(a.canvasColor, b.canvasColor, t),
1199 1200
      accentColor: Color.lerp(a.accentColor, b.accentColor, t),
      accentColorBrightness: t < 0.5 ? a.accentColorBrightness : b.accentColorBrightness,
1201
      scaffoldBackgroundColor: Color.lerp(a.scaffoldBackgroundColor, b.scaffoldBackgroundColor, t),
1202
      bottomAppBarColor: Color.lerp(a.bottomAppBarColor, b.bottomAppBarColor, t),
1203 1204
      cardColor: Color.lerp(a.cardColor, b.cardColor, t),
      dividerColor: Color.lerp(a.dividerColor, b.dividerColor, t),
1205 1206
      focusColor: Color.lerp(a.focusColor, b.focusColor, t),
      hoverColor: Color.lerp(a.hoverColor, b.hoverColor, t),
1207 1208
      highlightColor: Color.lerp(a.highlightColor, b.highlightColor, t),
      splashColor: Color.lerp(a.splashColor, b.splashColor, t),
1209
      splashFactory: t < 0.5 ? a.splashFactory : b.splashFactory,
1210 1211 1212
      selectedRowColor: Color.lerp(a.selectedRowColor, b.selectedRowColor, t),
      unselectedWidgetColor: Color.lerp(a.unselectedWidgetColor, b.unselectedWidgetColor, t),
      disabledColor: Color.lerp(a.disabledColor, b.disabledColor, t),
1213
      buttonTheme: t < 0.5 ? a.buttonTheme : b.buttonTheme,
1214
      toggleButtonsTheme: ToggleButtonsThemeData.lerp(a.toggleButtonsTheme, b.toggleButtonsTheme, t),
1215
      buttonColor: Color.lerp(a.buttonColor, b.buttonColor, t),
1216 1217
      secondaryHeaderColor: Color.lerp(a.secondaryHeaderColor, b.secondaryHeaderColor, t),
      textSelectionColor: Color.lerp(a.textSelectionColor, b.textSelectionColor, t),
1218
      cursorColor: Color.lerp(a.cursorColor, b.cursorColor, t),
1219 1220 1221 1222 1223 1224
      textSelectionHandleColor: Color.lerp(a.textSelectionHandleColor, b.textSelectionHandleColor, t),
      backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
      dialogBackgroundColor: Color.lerp(a.dialogBackgroundColor, b.dialogBackgroundColor, t),
      indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t),
      hintColor: Color.lerp(a.hintColor, b.hintColor, t),
      errorColor: Color.lerp(a.errorColor, b.errorColor, t),
1225
      toggleableActiveColor: Color.lerp(a.toggleableActiveColor, b.toggleableActiveColor, t),
1226 1227 1228
      textTheme: TextTheme.lerp(a.textTheme, b.textTheme, t),
      primaryTextTheme: TextTheme.lerp(a.primaryTextTheme, b.primaryTextTheme, t),
      accentTextTheme: TextTheme.lerp(a.accentTextTheme, b.accentTextTheme, t),
1229
      inputDecorationTheme: t < 0.5 ? a.inputDecorationTheme : b.inputDecorationTheme,
1230 1231 1232
      iconTheme: IconThemeData.lerp(a.iconTheme, b.iconTheme, t),
      primaryIconTheme: IconThemeData.lerp(a.primaryIconTheme, b.primaryIconTheme, t),
      accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t),
1233
      sliderTheme: SliderThemeData.lerp(a.sliderTheme, b.sliderTheme, t),
1234
      tabBarTheme: TabBarTheme.lerp(a.tabBarTheme, b.tabBarTheme, t),
1235
      tooltipTheme: TooltipThemeData.lerp(a.tooltipTheme, b.tooltipTheme, t),
1236
      cardTheme: CardTheme.lerp(a.cardTheme, b.cardTheme, t),
1237
      chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t),
1238
      platform: t < 0.5 ? a.platform : b.platform,
1239
      materialTapTargetSize: t < 0.5 ? a.materialTapTargetSize : b.materialTapTargetSize,
1240
      applyElevationOverlayColor: t < 0.5 ? a.applyElevationOverlayColor : b.applyElevationOverlayColor,
1241
      pageTransitionsTheme: t < 0.5 ? a.pageTransitionsTheme : b.pageTransitionsTheme,
1242
      appBarTheme: AppBarTheme.lerp(a.appBarTheme, b.appBarTheme, t),
1243
      bottomAppBarTheme: BottomAppBarTheme.lerp(a.bottomAppBarTheme, b.bottomAppBarTheme, t),
1244
      colorScheme: ColorScheme.lerp(a.colorScheme, b.colorScheme, t),
1245
      dialogTheme: DialogTheme.lerp(a.dialogTheme, b.dialogTheme, t),
1246
      floatingActionButtonTheme: FloatingActionButtonThemeData.lerp(a.floatingActionButtonTheme, b.floatingActionButtonTheme, t),
1247
      typography: Typography.lerp(a.typography, b.typography, t),
xster's avatar
xster committed
1248
      cupertinoOverrideTheme: t < 0.5 ? a.cupertinoOverrideTheme : b.cupertinoOverrideTheme,
1249
      snackBarTheme: SnackBarThemeData.lerp(a.snackBarTheme, b.snackBarTheme, t),
1250
      bottomSheetTheme: BottomSheetThemeData.lerp(a.bottomSheetTheme, b.bottomSheetTheme, t),
1251
      popupMenuTheme: PopupMenuThemeData.lerp(a.popupMenuTheme, b.popupMenuTheme, t),
1252
      bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t),
1253
      dividerTheme: DividerThemeData.lerp(a.dividerTheme, b.dividerTheme, t),
1254
      buttonBarTheme: ButtonBarThemeData.lerp(a.buttonBarTheme, b.buttonBarTheme, t),
1255 1256 1257
    );
  }

1258
  @override
1259
  bool operator ==(Object other) {
1260 1261
    if (other.runtimeType != runtimeType)
      return false;
1262
    final ThemeData otherData = other;
1263 1264 1265
    // 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.
1266
    return (otherData.brightness == brightness) &&
1267 1268
           (otherData.primaryColor == primaryColor) &&
           (otherData.primaryColorBrightness == primaryColorBrightness) &&
1269 1270 1271 1272
           (otherData.primaryColorLight == primaryColorLight) &&
           (otherData.primaryColorDark == primaryColorDark) &&
           (otherData.accentColor == accentColor) &&
           (otherData.accentColorBrightness == accentColorBrightness) &&
1273
           (otherData.canvasColor == canvasColor) &&
1274
           (otherData.scaffoldBackgroundColor == scaffoldBackgroundColor) &&
1275
           (otherData.bottomAppBarColor == bottomAppBarColor) &&
1276 1277 1278
           (otherData.cardColor == cardColor) &&
           (otherData.dividerColor == dividerColor) &&
           (otherData.highlightColor == highlightColor) &&
1279
           (otherData.splashColor == splashColor) &&
1280
           (otherData.splashFactory == splashFactory) &&
1281 1282
           (otherData.selectedRowColor == selectedRowColor) &&
           (otherData.unselectedWidgetColor == unselectedWidgetColor) &&
1283
           (otherData.disabledColor == disabledColor) &&
1284
           (otherData.buttonTheme == buttonTheme) &&
1285
           (otherData.buttonColor == buttonColor) &&
1286
           (otherData.toggleButtonsTheme == toggleButtonsTheme) &&
1287
           (otherData.secondaryHeaderColor == secondaryHeaderColor) &&
1288
           (otherData.textSelectionColor == textSelectionColor) &&
1289
           (otherData.cursorColor == cursorColor) &&
1290
           (otherData.textSelectionHandleColor == textSelectionHandleColor) &&
1291
           (otherData.backgroundColor == backgroundColor) &&
1292
           (otherData.dialogBackgroundColor == dialogBackgroundColor) &&
1293 1294
           (otherData.indicatorColor == indicatorColor) &&
           (otherData.hintColor == hintColor) &&
1295
           (otherData.errorColor == errorColor) &&
1296
           (otherData.toggleableActiveColor == toggleableActiveColor) &&
1297
           (otherData.textTheme == textTheme) &&
1298
           (otherData.primaryTextTheme == primaryTextTheme) &&
1299
           (otherData.accentTextTheme == accentTextTheme) &&
1300
           (otherData.inputDecorationTheme == inputDecorationTheme) &&
Ian Hickson's avatar
Ian Hickson committed
1301
           (otherData.iconTheme == iconTheme) &&
1302
           (otherData.primaryIconTheme == primaryIconTheme) &&
1303
           (otherData.accentIconTheme == accentIconTheme) &&
1304
           (otherData.sliderTheme == sliderTheme) &&
1305
           (otherData.tabBarTheme == tabBarTheme) &&
1306
           (otherData.tooltipTheme == tooltipTheme) &&
1307
           (otherData.cardTheme == cardTheme) &&
1308
           (otherData.chipTheme == chipTheme) &&
1309
           (otherData.platform == platform) &&
1310
           (otherData.materialTapTargetSize == materialTapTargetSize) &&
1311
           (otherData.applyElevationOverlayColor == applyElevationOverlayColor) &&
1312
           (otherData.pageTransitionsTheme == pageTransitionsTheme) &&
1313
           (otherData.appBarTheme == appBarTheme) &&
1314
           (otherData.bottomAppBarTheme == bottomAppBarTheme) &&
1315
           (otherData.colorScheme == colorScheme) &&
1316
           (otherData.dialogTheme == dialogTheme) &&
1317
           (otherData.floatingActionButtonTheme == floatingActionButtonTheme) &&
xster's avatar
xster committed
1318
           (otherData.typography == typography) &&
1319
           (otherData.cupertinoOverrideTheme == cupertinoOverrideTheme) &&
1320
           (otherData.snackBarTheme == snackBarTheme) &&
1321
           (otherData.bottomSheetTheme == bottomSheetTheme) &&
1322
           (otherData.popupMenuTheme == popupMenuTheme) &&
1323
           (otherData.bannerTheme == bannerTheme) &&
1324 1325
           (otherData.dividerTheme == dividerTheme) &&
           (otherData.buttonBarTheme == buttonBarTheme);
1326
  }
1327 1328

  @override
1329
  int get hashCode {
1330 1331 1332 1333
    // Warning: For the sanity of the reader, please make sure these properties
    // are in the exact same order as in operator == and in the raw constructor
    // and in the order of fields in the class and in the lerp() method.
    final List<Object> values = <Object>[
1334 1335 1336
      brightness,
      primaryColor,
      primaryColorBrightness,
1337 1338 1339 1340
      primaryColorLight,
      primaryColorDark,
      accentColor,
      accentColorBrightness,
1341 1342 1343 1344 1345
      canvasColor,
      scaffoldBackgroundColor,
      bottomAppBarColor,
      cardColor,
      dividerColor,
1346 1347
      focusColor,
      hoverColor,
1348 1349 1350 1351 1352
      highlightColor,
      splashColor,
      splashFactory,
      selectedRowColor,
      unselectedWidgetColor,
1353
      disabledColor,
1354
      buttonTheme,
1355
      buttonColor,
1356
      toggleButtonsTheme,
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375
      toggleableActiveColor,
      secondaryHeaderColor,
      textSelectionColor,
      cursorColor,
      textSelectionHandleColor,
      backgroundColor,
      dialogBackgroundColor,
      indicatorColor,
      hintColor,
      errorColor,
      textTheme,
      primaryTextTheme,
      accentTextTheme,
      inputDecorationTheme,
      iconTheme,
      primaryIconTheme,
      accentIconTheme,
      sliderTheme,
      tabBarTheme,
1376
      tooltipTheme,
1377 1378 1379 1380
      cardTheme,
      chipTheme,
      platform,
      materialTapTargetSize,
1381
      applyElevationOverlayColor,
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
      pageTransitionsTheme,
      appBarTheme,
      bottomAppBarTheme,
      colorScheme,
      dialogTheme,
      floatingActionButtonTheme,
      typography,
      cupertinoOverrideTheme,
      snackBarTheme,
      bottomSheetTheme,
1392
      popupMenuTheme,
1393
      bannerTheme,
1394
      dividerTheme,
1395
      buttonBarTheme,
1396 1397
    ];
    return hashList(values);
1398
  }
Hixie's avatar
Hixie committed
1399

1400
  @override
1401 1402
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1403 1404 1405
    final ThemeData defaultData = ThemeData.fallback();
    properties.add(EnumProperty<TargetPlatform>('platform', platform, defaultValue: defaultTargetPlatform));
    properties.add(EnumProperty<Brightness>('brightness', brightness, defaultValue: defaultData.brightness));
1406
    properties.add(ColorProperty('primaryColor', primaryColor, defaultValue: defaultData.primaryColor));
1407
    properties.add(EnumProperty<Brightness>('primaryColorBrightness', primaryColorBrightness, defaultValue: defaultData.primaryColorBrightness));
1408
    properties.add(ColorProperty('accentColor', accentColor, defaultValue: defaultData.accentColor));
1409
    properties.add(EnumProperty<Brightness>('accentColorBrightness', accentColorBrightness, defaultValue: defaultData.accentColorBrightness));
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
    properties.add(ColorProperty('canvasColor', canvasColor, defaultValue: defaultData.canvasColor));
    properties.add(ColorProperty('scaffoldBackgroundColor', scaffoldBackgroundColor, defaultValue: defaultData.scaffoldBackgroundColor));
    properties.add(ColorProperty('bottomAppBarColor', bottomAppBarColor, defaultValue: defaultData.bottomAppBarColor));
    properties.add(ColorProperty('cardColor', cardColor, defaultValue: defaultData.cardColor));
    properties.add(ColorProperty('dividerColor', dividerColor, defaultValue: defaultData.dividerColor));
    properties.add(ColorProperty('focusColor', focusColor, defaultValue: defaultData.focusColor));
    properties.add(ColorProperty('hoverColor', hoverColor, defaultValue: defaultData.hoverColor));
    properties.add(ColorProperty('highlightColor', highlightColor, defaultValue: defaultData.highlightColor));
    properties.add(ColorProperty('splashColor', splashColor, defaultValue: defaultData.splashColor));
    properties.add(ColorProperty('selectedRowColor', selectedRowColor, defaultValue: defaultData.selectedRowColor));
    properties.add(ColorProperty('unselectedWidgetColor', unselectedWidgetColor, defaultValue: defaultData.unselectedWidgetColor));
    properties.add(ColorProperty('disabledColor', disabledColor, defaultValue: defaultData.disabledColor));
    properties.add(ColorProperty('buttonColor', buttonColor, defaultValue: defaultData.buttonColor));
    properties.add(ColorProperty('secondaryHeaderColor', secondaryHeaderColor, defaultValue: defaultData.secondaryHeaderColor));
    properties.add(ColorProperty('textSelectionColor', textSelectionColor, defaultValue: defaultData.textSelectionColor));
    properties.add(ColorProperty('cursorColor', cursorColor, defaultValue: defaultData.cursorColor));
    properties.add(ColorProperty('textSelectionHandleColor', textSelectionHandleColor, defaultValue: defaultData.textSelectionHandleColor));
    properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor));
    properties.add(ColorProperty('dialogBackgroundColor', dialogBackgroundColor, defaultValue: defaultData.dialogBackgroundColor));
    properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: defaultData.indicatorColor));
    properties.add(ColorProperty('hintColor', hintColor, defaultValue: defaultData.hintColor));
    properties.add(ColorProperty('errorColor', errorColor, defaultValue: defaultData.errorColor));
    properties.add(ColorProperty('toggleableActiveColor', toggleableActiveColor, defaultValue: defaultData.toggleableActiveColor));
1433
    properties.add(DiagnosticsProperty<ButtonThemeData>('buttonTheme', buttonTheme));
1434
    properties.add(DiagnosticsProperty<ToggleButtonsThemeData>('toggleButtonsTheme', toggleButtonsTheme));
1435 1436 1437 1438 1439 1440 1441 1442
    properties.add(DiagnosticsProperty<TextTheme>('textTheme', textTheme));
    properties.add(DiagnosticsProperty<TextTheme>('primaryTextTheme', primaryTextTheme));
    properties.add(DiagnosticsProperty<TextTheme>('accentTextTheme', accentTextTheme));
    properties.add(DiagnosticsProperty<InputDecorationTheme>('inputDecorationTheme', inputDecorationTheme));
    properties.add(DiagnosticsProperty<IconThemeData>('iconTheme', iconTheme));
    properties.add(DiagnosticsProperty<IconThemeData>('primaryIconTheme', primaryIconTheme));
    properties.add(DiagnosticsProperty<IconThemeData>('accentIconTheme', accentIconTheme));
    properties.add(DiagnosticsProperty<SliderThemeData>('sliderTheme', sliderTheme));
1443
    properties.add(DiagnosticsProperty<TabBarTheme>('tabBarTheme', tabBarTheme));
1444
    properties.add(DiagnosticsProperty<TooltipThemeData>('tooltipTheme', tooltipTheme));
1445
    properties.add(DiagnosticsProperty<CardTheme>('cardTheme', cardTheme));
1446 1447
    properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme));
    properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize));
1448
    properties.add(DiagnosticsProperty<bool>('applyElevationOverlayColor', applyElevationOverlayColor));
1449
    properties.add(DiagnosticsProperty<PageTransitionsTheme>('pageTransitionsTheme', pageTransitionsTheme));
1450
    properties.add(DiagnosticsProperty<AppBarTheme>('appBarTheme', appBarTheme, defaultValue: defaultData.appBarTheme));
1451
    properties.add(DiagnosticsProperty<BottomAppBarTheme>('bottomAppBarTheme', bottomAppBarTheme, defaultValue: defaultData.bottomAppBarTheme));
1452
    properties.add(DiagnosticsProperty<ColorScheme>('colorScheme', colorScheme, defaultValue: defaultData.colorScheme));
1453
    properties.add(DiagnosticsProperty<DialogTheme>('dialogTheme', dialogTheme, defaultValue: defaultData.dialogTheme));
1454
    properties.add(DiagnosticsProperty<FloatingActionButtonThemeData>('floatingActionButtonThemeData', floatingActionButtonTheme, defaultValue: defaultData.floatingActionButtonTheme));
1455
    properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography));
xster's avatar
xster committed
1456
    properties.add(DiagnosticsProperty<CupertinoThemeData>('cupertinoOverrideTheme', cupertinoOverrideTheme, defaultValue: defaultData.cupertinoOverrideTheme));
1457
    properties.add(DiagnosticsProperty<SnackBarThemeData>('snackBarTheme', snackBarTheme, defaultValue: defaultData.snackBarTheme));
1458
    properties.add(DiagnosticsProperty<BottomSheetThemeData>('bottomSheetTheme', bottomSheetTheme, defaultValue: defaultData.bottomSheetTheme));
1459
    properties.add(DiagnosticsProperty<PopupMenuThemeData>('popupMenuTheme', popupMenuTheme, defaultValue: defaultData.popupMenuTheme));
1460
    properties.add(DiagnosticsProperty<MaterialBannerThemeData>('bannerTheme', bannerTheme, defaultValue: defaultData.bannerTheme));
1461
    properties.add(DiagnosticsProperty<DividerThemeData>('dividerTheme', dividerTheme, defaultValue: defaultData.dividerTheme));
1462
    properties.add(DiagnosticsProperty<ButtonBarThemeData>('buttonBarTheme', buttonBarTheme, defaultValue: defaultData.buttonBarTheme));
xster's avatar
xster committed
1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500
  }
}

/// A [CupertinoThemeData] that defers unspecified theme attributes to an
/// upstream Material [ThemeData].
///
/// This type of [CupertinoThemeData] is used by the Material [Theme] to
/// harmonize the [CupertinoTheme] with the material theme's colors and text
/// styles.
///
/// In the most basic case, [ThemeData]'s `cupertinoOverrideTheme` is null and
/// and descendant Cupertino widgets' styling is derived from the Material theme.
///
/// To override individual parts of the Material-derived Cupertino styling,
/// `cupertinoOverrideTheme`'s construction parameters can be used.
///
/// To completely decouple the Cupertino styling from Material theme derivation,
/// another [CupertinoTheme] widget can be inserted as a descendant of the
/// Material [Theme]. On a [MaterialApp], this can be done using the `builder`
/// parameter on the constructor.
///
/// See also:
///
///  * [CupertinoThemeData], whose null constructor parameters default to
///    reasonable iOS styling defaults rather than harmonizing with a Material
///    theme.
///  * [Theme], widget which inserts a [CupertinoTheme] with this
///    [MaterialBasedCupertinoThemeData].
// This class subclasses CupertinoThemeData rather than composes one because it
// _is_ a CupertinoThemeData with partially altered behavior. e.g. its textTheme
// is from the superclass and based on the primaryColor but the primaryColor
// comes from the Material theme unless overridden.
class MaterialBasedCupertinoThemeData extends CupertinoThemeData {
  /// Create a [MaterialBasedCupertinoThemeData] based on a Material [ThemeData]
  /// and its `cupertinoOverrideTheme`.
  ///
  /// The [materialTheme] parameter must not be null.
  MaterialBasedCupertinoThemeData({
1501
    @required ThemeData materialTheme,
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522
  }) : this._(
    materialTheme,
    (materialTheme.cupertinoOverrideTheme ?? const CupertinoThemeData()).noDefault(),
  );

  MaterialBasedCupertinoThemeData._(
    this._materialTheme,
    this._cupertinoOverrideTheme,
  ) : assert(_materialTheme != null),
      assert(_cupertinoOverrideTheme != null),
      // Pass all values to the superclass so Material-agnostic properties
      // like barBackgroundColor can still behave like a normal
      // CupertinoThemeData.
      super.raw(
        _cupertinoOverrideTheme.brightness,
        _cupertinoOverrideTheme.primaryColor,
        _cupertinoOverrideTheme.primaryContrastingColor,
        _cupertinoOverrideTheme.textTheme,
        _cupertinoOverrideTheme.barBackgroundColor,
        _cupertinoOverrideTheme.scaffoldBackgroundColor,
      );
xster's avatar
xster committed
1523 1524

  final ThemeData _materialTheme;
1525
  final CupertinoThemeData _cupertinoOverrideTheme;
xster's avatar
xster committed
1526 1527

  @override
1528
  Brightness get brightness => _cupertinoOverrideTheme.brightness ?? _materialTheme.brightness;
xster's avatar
xster committed
1529 1530

  @override
1531
  Color get primaryColor => _cupertinoOverrideTheme.primaryColor ?? _materialTheme.colorScheme.primary;
xster's avatar
xster committed
1532 1533

  @override
1534
  Color get primaryContrastingColor => _cupertinoOverrideTheme.primaryContrastingColor ?? _materialTheme.colorScheme.onPrimary;
xster's avatar
xster committed
1535 1536

  @override
1537
  Color get scaffoldBackgroundColor => _cupertinoOverrideTheme.scaffoldBackgroundColor ?? _materialTheme.scaffoldBackgroundColor;
xster's avatar
xster committed
1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550

  /// Copies the [ThemeData]'s `cupertinoOverrideTheme`.
  ///
  /// Only the specified override attributes of the [ThemeData]'s
  /// `cupertinoOverrideTheme` and the newly specified parameters are in the
  /// returned [CupertinoThemeData]. No derived attributes from iOS defaults or
  /// from cascaded Material theme attributes are copied.
  ///
  /// [MaterialBasedCupertinoThemeData.copyWith] cannot change the base
  /// Material [ThemeData]. To change the base Material [ThemeData], create a
  /// new Material [Theme] and use `copyWith` on the Material [ThemeData]
  /// instead.
  @override
1551
  MaterialBasedCupertinoThemeData copyWith({
xster's avatar
xster committed
1552 1553 1554 1555 1556 1557 1558
    Brightness brightness,
    Color primaryColor,
    Color primaryContrastingColor,
    CupertinoTextThemeData textTheme,
    Color barBackgroundColor,
    Color scaffoldBackgroundColor,
  }) {
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578
    return MaterialBasedCupertinoThemeData._(
      _materialTheme,
      _cupertinoOverrideTheme.copyWith(
        brightness: brightness,
        primaryColor: primaryColor,
        primaryContrastingColor: primaryContrastingColor,
        textTheme: textTheme,
        barBackgroundColor: barBackgroundColor,
        scaffoldBackgroundColor: scaffoldBackgroundColor,
      ),
    );
  }

  @override
  CupertinoThemeData resolveFrom(BuildContext context, { bool nullOk = false }) {
    // Only the cupertino override theme part will be resolved.
    // If the color comes from the material theme it's not resolved.
    return MaterialBasedCupertinoThemeData._(
      _materialTheme,
      _cupertinoOverrideTheme.resolveFrom(context, nullOk: nullOk),
xster's avatar
xster committed
1579
    );
1580
  }
1581
}
1582

1583 1584
class _IdentityThemeDataCacheKey {
  _IdentityThemeDataCacheKey(this.baseTheme, this.localTextGeometry);
1585

1586
  final ThemeData baseTheme;
1587 1588
  final TextTheme localTextGeometry;

1589 1590
  // Using XOR to make the hash function as fast as possible (e.g. Jenkins is
  // noticeably slower).
1591
  @override
1592
  int get hashCode => identityHashCode(baseTheme) ^ identityHashCode(localTextGeometry);
1593 1594

  @override
1595 1596 1597 1598
  bool operator ==(Object other) {
    // We are explicitly ignoring the possibility that the types might not
    // match in the interests of speed.
    final _IdentityThemeDataCacheKey otherKey = other;
1599
    return identical(baseTheme, otherKey.baseTheme) && identical(localTextGeometry, otherKey.localTextGeometry);
1600 1601 1602
  }
}

1603 1604 1605 1606 1607 1608
/// Cache of objects of limited size that uses the first in first out eviction
/// strategy (a.k.a least recently inserted).
///
/// The key that was inserted before all other keys is evicted first, i.e. the
/// one inserted least recently.
class _FifoCache<K, V> {
1609
  _FifoCache(this._maximumSize) : assert(_maximumSize != null && _maximumSize > 0);
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635

  /// In Dart the map literal uses a linked hash-map implementation, whose keys
  /// are stored such that [Map.keys] returns them in the order they were
  /// inserted.
  final Map<K, V> _cache = <K, V>{};

  /// Maximum number of entries to store in the cache.
  ///
  /// Once this many entries have been cached, the entry inserted least recently
  /// is evicted when adding a new entry.
  final int _maximumSize;

  /// Returns the previously cached value for the given key, if available;
  /// if not, calls the given callback to obtain it first.
  ///
  /// The arguments must not be null.
  V putIfAbsent(K key, V loader()) {
    assert(key != null);
    assert(loader != null);
    final V result = _cache[key];
    if (result != null)
      return result;
    if (_cache.length == _maximumSize)
      _cache.remove(_cache.keys.first);
    return _cache[key] = loader();
  }
1636
}