Unverified Commit 4ac2daf3 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

CupertinoTheme & CupertinoTextTheme dark mode updates (#41859)

parent 20e015ff
...@@ -17,6 +17,7 @@ export 'src/cupertino/button.dart'; ...@@ -17,6 +17,7 @@ export 'src/cupertino/button.dart';
export 'src/cupertino/colors.dart'; export 'src/cupertino/colors.dart';
export 'src/cupertino/date_picker.dart'; export 'src/cupertino/date_picker.dart';
export 'src/cupertino/dialog.dart'; export 'src/cupertino/dialog.dart';
export 'src/cupertino/icon_theme_data.dart';
export 'src/cupertino/icons.dart'; export 'src/cupertino/icons.dart';
export 'src/cupertino/interface_level.dart'; export 'src/cupertino/interface_level.dart';
export 'src/cupertino/localizations.dart'; export 'src/cupertino/localizations.dart';
......
...@@ -13,8 +13,7 @@ import 'theme.dart'; ...@@ -13,8 +13,7 @@ import 'theme.dart';
// Examples can assume: // Examples can assume:
// Widget child; // Widget child;
// Color lightModeColor; // BuildContext context;
// Color darkModeColor;
/// A palette of [Color] constants that describe colors commonly used when /// A palette of [Color] constants that describe colors commonly used when
/// matching the iOS platform aesthetics. /// matching the iOS platform aesthetics.
...@@ -539,45 +538,105 @@ class CupertinoColors { ...@@ -539,45 +538,105 @@ class CupertinoColors {
/// A [Color] subclass that represents a family of colors, and the currect effective /// A [Color] subclass that represents a family of colors, and the currect effective
/// color in the color family. /// color in the color family.
/// ///
/// When used as a regular color, `CupertinoDynamicColor` is equivalent to the /// When used as a regular color, [CupertinoDynamicColor] is equivalent to the
/// effective color (i.e. [CupertinoDynamicColor.value] will come from the effective /// effective color (i.e. [CupertinoDynamicColor.value] will come from the effective
/// color), which is determined by the [BuildContext] it is last resolved against. /// color), which is determined by the [BuildContext] it is last resolved against.
/// If it has never been resolved, the light, normal contrast, base elevation variant /// If it has never been resolved, the light, normal contrast, base elevation variant
/// [CupertinoDynamicColor.color] will be the effective color. /// [CupertinoDynamicColor.color] will be the default effective color.
// TODO(LongCatIsLooong): publicize once all Cupertino components have adopted this. ///
// {@tool sample} /// Sometimes manually resolving a [CupertinoDynamicColor] is not necessary, because
// /// the Cupertino Library provides built-in support for it.
// The following snippet will create a [CupertinoButton] whose background color ///
// is _lightModeColor_ in light mode but _darkModeColor_ in dark mode. /// ### Using a [CupertinoDynamicColor] in a Cupertino widget
// ///
// /// When a Cupertino widget is provided with a [CupertinoDynamicColor], either
// ```dart /// directly in its constructor, or from an [InheritedWidget] it depends on (for example,
// CupertinoButton( /// [DefaultTextStyle]), the widget will automatically resolve the color using
// child: child, /// [CupertinoDynamicColor.resolve] against its own [BuildContext], on a best-effort
// color: CupertinoDynamicColor.withVibrancy( /// basis.
// color: lightModeColor, ///
// darkColor: darkModeColor, /// {@tool sample}
// ), /// By default a [CupertinoButton] has no background color. The following sample
// onTap: () => null, /// code shows how to build a [CupertinoButton] that appears white in light mode,
// ) /// and changes automatically to black in dark mode.
// ``` ///
// {@end-tool} /// ```dart
// /// CupertinoButton(
// When a Cupertino component is provided with a `CupertinoDynamicColor`, either /// child: child,
// directly in its constructor, or from an [InheritedWidget] it depends on (for example, /// // CupertinoDynamicColor works out of box in a CupertinoButton.
// [DefaultTextStyle]), the component will automatically resolve the color by calling /// color: CupertinoDynamicColor.withBrightness(
// [CupertinoDynamicColor.resolve], using their own [BuildContext]. /// color: CupertinoColors.white,
// /// darkColor: CupertinoColors.black,
// When used outside of a Cupertino component, such color resolution will not happen /// ),
// automatically. It's essential to call [CupertinoDynamicColor.resolve] with the /// onPressed: () { },
// correct [BuildContext] before using the color to paint, in order to get the /// )
// desired effect. /// ```
/// {@end-tool}
///
/// ### Using a [CupertinoDynamicColor] from a [CupertinoTheme]
///
/// When referring to a [CupertinoTheme] color, generally the color will already
/// have adapted to the ambient [BuildContext], because [CupertinoTheme.of]
/// implicitly resolves all the colors used in the retrieved [CupertinoThemeData],
/// before returning it.
///
/// {@tool sample}
/// The following code sample creates a [Container] with the `primaryColor` of the
/// current theme. If `primaryColor` is a [CupertinoDynamicColor], the container
/// will be adaptive, thanks to [CupertinoTheme.of]: it will switch to `primaryColor`'s
/// dark variant once dark mode is turned on, and turns to primaryColor`'s high
/// contrast variant when [MediaQueryData.highContrast] is requested in the ambient
/// [MediaQuery], etc.
///
/// ```dart
/// Container(
/// // Container is not a Cupertino widget, but CupertinoTheme.of implicitly
/// // resolves colors used in the retrieved CupertinoThemeData.
/// color: CupertinoTheme.of(context).primaryColor,
/// )
/// ```
/// {@end-tool}
///
/// ### Manually Resolving a [CupertinoDynamicColor]
///
/// When used to configure a non-Cupertino widget, or wrapped in an object opaque
/// to the receiving Cupertino component, a [CupertinoDynamicColor] may need to be
/// manually resolved using [CupertinoDynamicColor.resolve], before it can used
/// to paint. For example, to use a custom [Border] in a [CupertinoNavigationBar],
/// the colors used in the [Border] have to be resolved manually before being passed
/// to [CupertinoNavigationBar]'s constructor.
///
/// {@tool sample}
///
/// The following code samples demostrate two cases where you have to manually
/// resolve a [CupertinoDynamicColor].
///
/// ```dart
/// CupertinoNavigationBar(
/// // CupertinoNavigationBar does not know how to resolve colors used in
/// // a Border class.
/// border: Border(
/// bottom: BorderSide(
/// color: CupertinoDynamicColor.resolve(CupertinoColors.systemBlue, context),
/// ),
/// ),
/// )
/// ```
///
/// ```dart
/// Container(
/// // Container is not a Cupertino widget.
/// color: CupertinoDynamicColor.resolve(CupertinoColors.systemBlue, context),
/// )
/// ```
/// {@end-tool}
/// ///
/// See also: /// See also:
/// ///
/// * [CupertinoUserInterfaceLevel], an [InheritedWidget] that may affect color /// * [CupertinoUserInterfaceLevel], an [InheritedWidget] that may affect color
/// resolution of a `CupertinoDynamicColor`. /// resolution of a [CupertinoDynamicColor].
/// * https://developer.apple.com/documentation/uikit/uicolor/3238042-resolvedcolor. /// * [CupertinoTheme.of], a static method that retrieves the ambient [CupertinoThemeData],
/// and then resolves [CupertinoDynamicColor]s used in the retrieved data.
@immutable @immutable
class CupertinoDynamicColor extends Color { class CupertinoDynamicColor extends Color {
/// Creates an adaptive [Color] that changes its effective color based on the /// Creates an adaptive [Color] that changes its effective color based on the
...@@ -678,7 +737,7 @@ class CupertinoDynamicColor extends Color { ...@@ -678,7 +737,7 @@ class CupertinoDynamicColor extends Color {
/// The color to use when the [BuildContext] implies a combination of light mode, /// The color to use when the [BuildContext] implies a combination of light mode,
/// normal contrast, and base interface elevation. /// normal contrast, and base interface elevation.
/// ///
/// In other words, this color will be the effective color of the `CupertinoDynamicColor` /// In other words, this color will be the effective color of the [CupertinoDynamicColor]
/// after it is resolved against a [BuildContext] that: /// after it is resolved against a [BuildContext] that:
/// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.light], /// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.light],
/// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.light]. /// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.light].
...@@ -689,7 +748,7 @@ class CupertinoDynamicColor extends Color { ...@@ -689,7 +748,7 @@ class CupertinoDynamicColor extends Color {
/// The color to use when the [BuildContext] implies a combination of dark mode, /// The color to use when the [BuildContext] implies a combination of dark mode,
/// normal contrast, and base interface elevation. /// normal contrast, and base interface elevation.
/// ///
/// In other words, this color will be the effective color of the `CupertinoDynamicColor` /// In other words, this color will be the effective color of the [CupertinoDynamicColor]
/// after it is resolved against a [BuildContext] that: /// after it is resolved against a [BuildContext] that:
/// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.dark], /// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.dark],
/// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.dark]. /// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.dark].
...@@ -700,7 +759,7 @@ class CupertinoDynamicColor extends Color { ...@@ -700,7 +759,7 @@ class CupertinoDynamicColor extends Color {
/// The color to use when the [BuildContext] implies a combination of light mode, /// The color to use when the [BuildContext] implies a combination of light mode,
/// high contrast, and base interface elevation. /// high contrast, and base interface elevation.
/// ///
/// In other words, this color will be the effective color of the `CupertinoDynamicColor` /// In other words, this color will be the effective color of the [CupertinoDynamicColor]
/// after it is resolved against a [BuildContext] that: /// after it is resolved against a [BuildContext] that:
/// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.light], /// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.light],
/// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.light]. /// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.light].
...@@ -711,7 +770,7 @@ class CupertinoDynamicColor extends Color { ...@@ -711,7 +770,7 @@ class CupertinoDynamicColor extends Color {
/// The color to use when the [BuildContext] implies a combination of dark mode, /// The color to use when the [BuildContext] implies a combination of dark mode,
/// high contrast, and base interface elevation. /// high contrast, and base interface elevation.
/// ///
/// In other words, this color will be the effective color of the `CupertinoDynamicColor` /// In other words, this color will be the effective color of the [CupertinoDynamicColor]
/// after it is resolved against a [BuildContext] that: /// after it is resolved against a [BuildContext] that:
/// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.dark], /// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.dark],
/// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.dark]. /// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.dark].
...@@ -722,7 +781,7 @@ class CupertinoDynamicColor extends Color { ...@@ -722,7 +781,7 @@ class CupertinoDynamicColor extends Color {
/// The color to use when the [BuildContext] implies a combination of light mode, /// The color to use when the [BuildContext] implies a combination of light mode,
/// normal contrast, and elevated interface elevation. /// normal contrast, and elevated interface elevation.
/// ///
/// In other words, this color will be the effective color of the `CupertinoDynamicColor` /// In other words, this color will be the effective color of the [CupertinoDynamicColor]
/// after it is resolved against a [BuildContext] that: /// after it is resolved against a [BuildContext] that:
/// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.light], /// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.light],
/// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.light]. /// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.light].
...@@ -733,7 +792,7 @@ class CupertinoDynamicColor extends Color { ...@@ -733,7 +792,7 @@ class CupertinoDynamicColor extends Color {
/// The color to use when the [BuildContext] implies a combination of dark mode, /// The color to use when the [BuildContext] implies a combination of dark mode,
/// normal contrast, and elevated interface elevation. /// normal contrast, and elevated interface elevation.
/// ///
/// In other words, this color will be the effective color of the `CupertinoDynamicColor` /// In other words, this color will be the effective color of the [CupertinoDynamicColor]
/// after it is resolved against a [BuildContext] that: /// after it is resolved against a [BuildContext] that:
/// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.dark], /// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.dark],
/// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.dark]. /// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.dark].
...@@ -744,7 +803,7 @@ class CupertinoDynamicColor extends Color { ...@@ -744,7 +803,7 @@ class CupertinoDynamicColor extends Color {
/// The color to use when the [BuildContext] implies a combination of light mode, /// The color to use when the [BuildContext] implies a combination of light mode,
/// high contrast, and elevated interface elevation. /// high contrast, and elevated interface elevation.
/// ///
/// In other words, this color will be the effective color of the `CupertinoDynamicColor` /// In other words, this color will be the effective color of the [CupertinoDynamicColor]
/// after it is resolved against a [BuildContext] that: /// after it is resolved against a [BuildContext] that:
/// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.light], /// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.light],
/// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.light]. /// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.light].
...@@ -755,7 +814,7 @@ class CupertinoDynamicColor extends Color { ...@@ -755,7 +814,7 @@ class CupertinoDynamicColor extends Color {
/// The color to use when the [BuildContext] implies a combination of dark mode, /// The color to use when the [BuildContext] implies a combination of dark mode,
/// high contrast, and elevated interface elevation. /// high contrast, and elevated interface elevation.
/// ///
/// In other words, this color will be the effective color of the `CupertinoDynamicColor` /// In other words, this color will be the effective color of the [CupertinoDynamicColor]
/// after it is resolved against a [BuildContext] that: /// after it is resolved against a [BuildContext] that:
/// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.dark], /// - has a [CupertinoTheme] whose [brightness] is [PlatformBrightness.dark],
/// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.dark]. /// or a [MediaQuery] whose [MediaQueryData.platformBrightness] is [PlatformBrightness.dark].
...@@ -802,10 +861,10 @@ class CupertinoDynamicColor extends Color { ...@@ -802,10 +861,10 @@ class CupertinoDynamicColor extends Color {
|| darkHighContrastColor != darkHighContrastElevatedColor; || darkHighContrastColor != darkHighContrastElevatedColor;
} }
/// Resolves this `CupertinoDynamicColor` using the provided [BuildContext]. /// Resolves this [CupertinoDynamicColor] using the provided [BuildContext].
/// ///
/// Calling this method will create a new `CupertinoDynamicColor` that is almost /// Calling this method will create a new [CupertinoDynamicColor] that is almost
/// identical to this `CupertinoDynamicColor`, except the effective color is /// identical to this [CupertinoDynamicColor], except the effective color is
/// changed to adapt to the given [BuildContext]. /// changed to adapt to the given [BuildContext].
/// ///
/// For example, if the given [BuildContext] indicates the widgets in the subtree /// For example, if the given [BuildContext] indicates the widgets in the subtree
...@@ -814,9 +873,9 @@ class CupertinoDynamicColor extends Color { ...@@ -814,9 +873,9 @@ class CupertinoDynamicColor extends Color {
/// with a high accessibility contrast (the surrounding [MediaQuery]'s [MediaQueryData.highContrast] /// with a high accessibility contrast (the surrounding [MediaQuery]'s [MediaQueryData.highContrast]
/// is `true`), and an elevated interface elevation (the surrounding [CupertinoUserInterfaceLevel]'s /// is `true`), and an elevated interface elevation (the surrounding [CupertinoUserInterfaceLevel]'s
/// `data` is [CupertinoUserInterfaceLevelData.elevated]), the resolved /// `data` is [CupertinoUserInterfaceLevelData.elevated]), the resolved
/// `CupertinoDynamicColor` will be the same as this [CupertinoDynamicColor], /// [CupertinoDynamicColor] will be the same as this [CupertinoDynamicColor],
/// except its effective color will be the `darkHighContrastElevatedColor` variant /// except its effective color will be the `darkHighContrastElevatedColor` variant
/// from the orignal `CupertinoDynamicColor`. /// from the orignal [CupertinoDynamicColor].
/// ///
/// Calling this function may create dependencies on the closest instance of some /// Calling this function may create dependencies on the closest instance of some
/// [InheritedWidget]s that enclose the given [BuildContext]. E.g., if [darkColor] /// [InheritedWidget]s that enclose the given [BuildContext]. E.g., if [darkColor]
......
// Copyright 2019 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.
import 'package:flutter/widgets.dart';
import 'colors.dart';
/// An [IconThemeData] subclass that automatically resolves its [color] when retrieved
/// using [IconTheme.of].
class CupertinoIconThemeData extends IconThemeData {
/// Creates a [CupertinoIconThemeData].
///
/// The opacity applies to both explicit and default icon colors. The value
/// is clamped between 0.0 and 1.0.
const CupertinoIconThemeData({
Color color,
double opacity,
double size
}) : super(color: color, opacity: opacity, size: size);
/// Called by [IconThemeData.of] to resolve [color] against the given [BuildContext].
@override
IconThemeData resolve(BuildContext context) {
final Color resolvedColor = CupertinoDynamicColor.resolve(color, context);
return resolvedColor == color ? this : copyWith(color: resolvedColor);
}
}
...@@ -8,26 +8,24 @@ import 'package:flutter/widgets.dart'; ...@@ -8,26 +8,24 @@ import 'package:flutter/widgets.dart';
import 'colors.dart'; import 'colors.dart';
// Please update _DefaultCupertinoTextThemeData and _DefaultCupertinoTextThemeData
// accordingly after changing the default color here, as their implementation
// depends on the default value of the color field.
//
// Values derived from https://developer.apple.com/design/resources/. // Values derived from https://developer.apple.com/design/resources/.
const TextStyle _kDefaultLightTextStyle = TextStyle( const TextStyle _kDefaultTextStyle = TextStyle(
inherit: false, inherit: false,
fontFamily: '.SF Pro Text', fontFamily: '.SF Pro Text',
fontSize: 17.0, fontSize: 17.0,
letterSpacing: -0.41, letterSpacing: -0.41,
color: CupertinoColors.black, color: CupertinoColors.label,
decoration: TextDecoration.none,
);
// Values derived from https://developer.apple.com/design/resources/.
const TextStyle _kDefaultDarkTextStyle = TextStyle(
inherit: false,
fontFamily: '.SF Pro Text',
fontSize: 17.0,
letterSpacing: -0.41,
color: CupertinoColors.white,
decoration: TextDecoration.none, decoration: TextDecoration.none,
); );
// Please update _DefaultCupertinoTextThemeData and _DefaultCupertinoTextThemeData
// accordingly after changing the default color here, as their implementation
// depends on the default value of the color field.
//
// Values derived from https://developer.apple.com/design/resources/. // Values derived from https://developer.apple.com/design/resources/.
const TextStyle _kDefaultActionTextStyle = TextStyle( const TextStyle _kDefaultActionTextStyle = TextStyle(
inherit: false, inherit: false,
...@@ -38,6 +36,10 @@ const TextStyle _kDefaultActionTextStyle = TextStyle( ...@@ -38,6 +36,10 @@ const TextStyle _kDefaultActionTextStyle = TextStyle(
decoration: TextDecoration.none, decoration: TextDecoration.none,
); );
// Please update _DefaultCupertinoTextThemeData and _DefaultCupertinoTextThemeData
// accordingly after changing the default color here, as their implementation
// depends on the default value of the color field.
//
// Values derived from https://developer.apple.com/design/resources/. // Values derived from https://developer.apple.com/design/resources/.
const TextStyle _kDefaultTabLabelTextStyle = TextStyle( const TextStyle _kDefaultTabLabelTextStyle = TextStyle(
inherit: false, inherit: false,
...@@ -47,97 +49,83 @@ const TextStyle _kDefaultTabLabelTextStyle = TextStyle( ...@@ -47,97 +49,83 @@ const TextStyle _kDefaultTabLabelTextStyle = TextStyle(
color: CupertinoColors.inactiveGray, color: CupertinoColors.inactiveGray,
); );
const TextStyle _kDefaultMiddleTitleLightTextStyle = TextStyle( // Please update _DefaultCupertinoTextThemeData and _DefaultCupertinoTextThemeData
inherit: false, // accordingly after changing the default color here, as their implementation
fontFamily: '.SF Pro Text', // depends on the default value of the color field.
fontSize: 17.0, const TextStyle _kDefaultMiddleTitleTextStyle = TextStyle(
fontWeight: FontWeight.w600,
letterSpacing: -0.41,
color: CupertinoColors.black,
);
const TextStyle _kDefaultMiddleTitleDarkTextStyle = TextStyle(
inherit: false, inherit: false,
fontFamily: '.SF Pro Text', fontFamily: '.SF Pro Text',
fontSize: 17.0, fontSize: 17.0,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
letterSpacing: -0.41, letterSpacing: -0.41,
color: CupertinoColors.white, color: CupertinoColors.label,
); );
const TextStyle _kDefaultLargeTitleLightTextStyle = TextStyle( // Please update _DefaultCupertinoTextThemeData and _DefaultCupertinoTextThemeData
// accordingly after changing the default color here, as their implementation
// depends on the default value of the color field.
const TextStyle _kDefaultLargeTitleTextStyle = TextStyle(
inherit: false, inherit: false,
fontFamily: '.SF Pro Display', fontFamily: '.SF Pro Display',
fontSize: 34.0, fontSize: 34.0,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
letterSpacing: 0.41, letterSpacing: 0.41,
color: CupertinoColors.black, color: CupertinoColors.label,
); );
const TextStyle _kDefaultLargeTitleDarkTextStyle = TextStyle( // Please update _DefaultCupertinoTextThemeData and _DefaultCupertinoTextThemeData
inherit: false, // accordingly after changing the default color here, as their implementation
fontFamily: '.SF Pro Display', // depends on the default value of the color field.
fontSize: 34.0, //
fontWeight: FontWeight.w700, // Inspected on iOS 13 simulator with "Debug View Hierarchy".
letterSpacing: 0.41, // Value extracted from off-center labels. Centered labels have a font size of 25pt.
color: CupertinoColors.white, const TextStyle _kDefaultPickerTextStyle = TextStyle(
);
// Eyeballed value since it's not documented in https://developer.apple.com/design/resources/.
const TextStyle _kDefaultPickerLightTextStyle = TextStyle(
inherit: false,
fontFamily: '.SF Pro Display',
fontSize: 25.0,
fontWeight: FontWeight.w400,
letterSpacing: -0.41,
color: CupertinoColors.black,
);
// Eyeballed value since it's not documented in https://developer.apple.com/design/resources/.
const TextStyle _kDefaultPickerDarkTextStyle = TextStyle(
inherit: false, inherit: false,
fontFamily: '.SF Pro Display', fontFamily: '.SF Pro Display',
fontSize: 25.0, fontSize: 21.0,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
letterSpacing: -0.41, letterSpacing: -0.41,
color: CupertinoColors.white, color: CupertinoColors.label,
); );
// Eyeballed value since it's not documented in https://developer.apple.com/design/resources/. // Please update _DefaultCupertinoTextThemeData and _DefaultCupertinoTextThemeData
// accordingly after changing the default color here, as their implementation
// depends on the default value of the color field.
//
// Inspected on iOS 13 simulator with "Debug View Hierarchy". // Inspected on iOS 13 simulator with "Debug View Hierarchy".
const TextStyle _kDefaultDateTimePickerLightTextStyle = TextStyle( // Value extracted from off-center labels. Centered labels have a font size of 25pt.
const TextStyle _kDefaultDateTimePickerTextStyle = TextStyle(
inherit: false, inherit: false,
fontFamily: '.SF Pro Display', fontFamily: '.SF Pro Display',
fontSize: 21, fontSize: 21,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
color: CupertinoColors.black, color: CupertinoColors.label,
); );
// Eyeballed value since it's not documented in https://developer.apple.com/design/resources/. TextStyle _resolveTextStyle(TextStyle style, BuildContext context, bool nullOk) {
// Inspected on iOS 13 simulator with "Debug View Hierarchy". // This does not resolve the shadow color, foreground, background, etc.
const TextStyle _kDefaultDateTimePickerDarkTextStyle = TextStyle( return style?.copyWith(
inherit: false, color: CupertinoDynamicColor.resolve(style?.color, context, nullOk: nullOk),
fontFamily: '.SF Pro Display', backgroundColor: CupertinoDynamicColor.resolve(style?.backgroundColor, context, nullOk: nullOk),
fontSize: 21, decorationColor: CupertinoDynamicColor.resolve(style?.decorationColor, context, nullOk: nullOk),
fontWeight: FontWeight.normal, );
color: CupertinoColors.white, }
);
/// Cupertino typography theme in a [CupertinoThemeData]. /// Cupertino typography theme in a [CupertinoThemeData].
@immutable @immutable
class CupertinoTextThemeData extends Diagnosticable { class CupertinoTextThemeData extends Diagnosticable {
/// Create a [CupertinoTextThemeData]. /// Create a [CupertinoTextThemeData].
/// ///
/// The [primaryColor] and [isLight] parameters are used to derive TextStyle /// The [primaryColor] is used to derive TextStyle defaults of other attributes
/// defaults of other attributes such as [textStyle] and [actionTextStyle] /// such as [navActionTextStyle] and [actionTextStyle], it must not be null when
/// etc. The default value of [primaryColor] is [CupertinoColors.activeBlue] /// either [navActionTextStyle] or [actionTextStyle] is null. Defaults to
/// and the default value of [isLight] is true. /// [CupertinoColors.systemBlue].
/// ///
/// Other [TextStyle] parameters default to default iOS text styles when /// Other [TextStyle] parameters default to default iOS text styles when
/// unspecified. /// unspecified.
const CupertinoTextThemeData({ const CupertinoTextThemeData({
Color primaryColor, Color primaryColor = CupertinoColors.systemBlue,
Brightness brightness, @deprecated Brightness brightness, //ignore: avoid_unused_constructor_parameters , the parameter is deprecated.
TextStyle textStyle, TextStyle textStyle,
TextStyle actionTextStyle, TextStyle actionTextStyle,
TextStyle tabLabelTextStyle, TextStyle tabLabelTextStyle,
...@@ -146,96 +134,90 @@ class CupertinoTextThemeData extends Diagnosticable { ...@@ -146,96 +134,90 @@ class CupertinoTextThemeData extends Diagnosticable {
TextStyle navActionTextStyle, TextStyle navActionTextStyle,
TextStyle pickerTextStyle, TextStyle pickerTextStyle,
TextStyle dateTimePickerTextStyle, TextStyle dateTimePickerTextStyle,
}) : _primaryColor = primaryColor ?? CupertinoColors.activeBlue, }) : this._raw(
_brightness = brightness, const _DefaultCupertinoTextThemeData(),
_textStyle = textStyle, primaryColor,
_actionTextStyle = actionTextStyle, textStyle,
_tabLabelTextStyle = tabLabelTextStyle, actionTextStyle,
_navTitleTextStyle = navTitleTextStyle, tabLabelTextStyle,
_navLargeTitleTextStyle = navLargeTitleTextStyle, navTitleTextStyle,
_navActionTextStyle = navActionTextStyle, navLargeTitleTextStyle,
_pickerTextStyle = pickerTextStyle, navActionTextStyle,
_dateTimePickerTextStyle = dateTimePickerTextStyle; pickerTextStyle,
dateTimePickerTextStyle,
);
const CupertinoTextThemeData._raw(
this._defaults,
this._primaryColor,
this._textStyle,
this._actionTextStyle,
this._tabLabelTextStyle,
this._navTitleTextStyle,
this._navLargeTitleTextStyle,
this._navActionTextStyle,
this._pickerTextStyle,
this._dateTimePickerTextStyle,
) : assert((_navActionTextStyle != null && _actionTextStyle != null) || _primaryColor != null);
final _DefaultCupertinoTextThemeData _defaults;
final Color _primaryColor; final Color _primaryColor;
final Brightness _brightness;
bool get _isLight => _brightness != Brightness.dark;
final TextStyle _textStyle; final TextStyle _textStyle;
/// Typography of general text content for Cupertino widgets. /// The [TextStyle] of general text content for Cupertino widgets.
TextStyle get textStyle => _textStyle ?? (_isLight ? _kDefaultLightTextStyle : _kDefaultDarkTextStyle); TextStyle get textStyle => _textStyle ?? _defaults.textStyle;
final TextStyle _actionTextStyle; final TextStyle _actionTextStyle;
/// Typography of interactive text content such as text in a button without background. /// The [TextStyle] of interactive text content such as text in a button without background.
TextStyle get actionTextStyle { TextStyle get actionTextStyle {
return _actionTextStyle ?? _kDefaultActionTextStyle.copyWith( return _actionTextStyle ?? _defaults.actionTextStyle(primaryColor: _primaryColor);
color: _primaryColor,
);
} }
final TextStyle _tabLabelTextStyle; final TextStyle _tabLabelTextStyle;
/// Typography of unselected tabs. /// The [TextStyle] of unselected tabs.
TextStyle get tabLabelTextStyle => _tabLabelTextStyle ?? _kDefaultTabLabelTextStyle; TextStyle get tabLabelTextStyle => _tabLabelTextStyle ?? _defaults.tabLabelTextStyle;
final TextStyle _navTitleTextStyle; final TextStyle _navTitleTextStyle;
/// Typography of titles in standard navigation bars. /// The [TextStyle] of titles in standard navigation bars.
TextStyle get navTitleTextStyle { TextStyle get navTitleTextStyle => _navTitleTextStyle ?? _defaults.navTitleTextStyle;
return _navTitleTextStyle ??
(_isLight ? _kDefaultMiddleTitleLightTextStyle : _kDefaultMiddleTitleDarkTextStyle);
}
final TextStyle _navLargeTitleTextStyle; final TextStyle _navLargeTitleTextStyle;
/// Typography of large titles in sliver navigation bars. /// The [TextStyle] of large titles in sliver navigation bars.
TextStyle get navLargeTitleTextStyle { TextStyle get navLargeTitleTextStyle => _navLargeTitleTextStyle ?? _defaults.navLargeTitleTextStyle;
return _navLargeTitleTextStyle ??
(_isLight ? _kDefaultLargeTitleLightTextStyle : _kDefaultLargeTitleDarkTextStyle);
}
final TextStyle _navActionTextStyle; final TextStyle _navActionTextStyle;
/// Typography of interactive text content in navigation bars. /// The [TextStyle] of interactive text content in navigation bars.
TextStyle get navActionTextStyle { TextStyle get navActionTextStyle {
return _navActionTextStyle ?? _kDefaultActionTextStyle.copyWith( return _navActionTextStyle ?? _defaults.navActionTextStyle(primaryColor: _primaryColor);
color: _primaryColor,
);
} }
final TextStyle _pickerTextStyle; final TextStyle _pickerTextStyle;
/// Typography of pickers. /// The [TextStyle] of pickers.
TextStyle get pickerTextStyle { TextStyle get pickerTextStyle => _pickerTextStyle ?? _defaults.pickerTextStyle;
return _pickerTextStyle ??
(_isLight ? _kDefaultPickerLightTextStyle : _kDefaultPickerDarkTextStyle);
}
final TextStyle _dateTimePickerTextStyle; final TextStyle _dateTimePickerTextStyle;
/// Typography of date time pickers. /// The [TextStyle] of date time pickers.
TextStyle get dateTimePickerTextStyle { TextStyle get dateTimePickerTextStyle => _dateTimePickerTextStyle ?? _defaults.dateTimePickerTextStyle;
return _dateTimePickerTextStyle ??
(_isLight ? _kDefaultDateTimePickerLightTextStyle : _kDefaultDateTimePickerDarkTextStyle);
}
/// Returns a copy of the current [CupertinoTextThemeData] with all the colors /// Returns a copy of the current [CupertinoTextThemeData] with all the colors
/// resolved against the given [BuildContext]. /// resolved against the given [BuildContext].
///
/// Throws an exception if any of the [InheritedWidget]s required to resolve
/// this [CupertinoTextThemeData] is not found in [context], unless [nullOk] is
/// set to true, in which case [CupertinoDynamicColor]s that fail to resolve will
/// be used as-is.
CupertinoTextThemeData resolveFrom(BuildContext context, { bool nullOk = false }) { CupertinoTextThemeData resolveFrom(BuildContext context, { bool nullOk = false }) {
Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context, nullOk: nullOk); return CupertinoTextThemeData._raw(
_defaults?.resolveFrom(context, nullOk),
TextStyle resolveTextStyle(TextStyle textStyle) { CupertinoDynamicColor.resolve(_primaryColor, context, nullOk: nullOk),
return textStyle?.copyWith( _resolveTextStyle(_textStyle, context, nullOk),
color: convertColor(textStyle.color), _resolveTextStyle(_actionTextStyle, context, nullOk),
backgroundColor: convertColor(textStyle.backgroundColor), _resolveTextStyle(_tabLabelTextStyle, context, nullOk),
decorationColor: convertColor(textStyle.decorationColor), _resolveTextStyle(_navTitleTextStyle, context, nullOk),
); _resolveTextStyle(_navLargeTitleTextStyle, context, nullOk),
} _resolveTextStyle(_navActionTextStyle, context, nullOk),
_resolveTextStyle(_pickerTextStyle, context, nullOk),
return copyWith( _resolveTextStyle(_dateTimePickerTextStyle, context, nullOk),
primaryColor: convertColor(_primaryColor),
textStyle: resolveTextStyle(_textStyle),
actionTextStyle: resolveTextStyle(_actionTextStyle),
tabLabelTextStyle: resolveTextStyle(_tabLabelTextStyle),
navTitleTextStyle : resolveTextStyle(_navTitleTextStyle),
navLargeTitleTextStyle: resolveTextStyle(_navLargeTitleTextStyle),
navActionTextStyle: resolveTextStyle(_navActionTextStyle),
pickerTextStyle: resolveTextStyle(_pickerTextStyle),
dateTimePickerTextStyle: resolveTextStyle(_dateTimePickerTextStyle),
); );
} }
...@@ -243,7 +225,7 @@ class CupertinoTextThemeData extends Diagnosticable { ...@@ -243,7 +225,7 @@ class CupertinoTextThemeData extends Diagnosticable {
/// specified overrides. /// specified overrides.
CupertinoTextThemeData copyWith({ CupertinoTextThemeData copyWith({
Color primaryColor, Color primaryColor,
Brightness brightness, @deprecated Brightness brightness,
TextStyle textStyle, TextStyle textStyle,
TextStyle actionTextStyle, TextStyle actionTextStyle,
TextStyle tabLabelTextStyle, TextStyle tabLabelTextStyle,
...@@ -253,17 +235,53 @@ class CupertinoTextThemeData extends Diagnosticable { ...@@ -253,17 +235,53 @@ class CupertinoTextThemeData extends Diagnosticable {
TextStyle pickerTextStyle, TextStyle pickerTextStyle,
TextStyle dateTimePickerTextStyle, TextStyle dateTimePickerTextStyle,
}) { }) {
return CupertinoTextThemeData( return CupertinoTextThemeData._raw(
primaryColor: primaryColor ?? _primaryColor, _defaults,
brightness: brightness ?? _brightness, primaryColor ?? _primaryColor,
textStyle: textStyle ?? _textStyle, textStyle ?? _textStyle,
actionTextStyle: actionTextStyle ?? _actionTextStyle, actionTextStyle ?? _actionTextStyle,
tabLabelTextStyle: tabLabelTextStyle ?? _tabLabelTextStyle, tabLabelTextStyle ?? _tabLabelTextStyle,
navTitleTextStyle: navTitleTextStyle ?? _navTitleTextStyle, navTitleTextStyle ?? _navTitleTextStyle,
navLargeTitleTextStyle: navLargeTitleTextStyle ?? _navLargeTitleTextStyle, navLargeTitleTextStyle ?? _navLargeTitleTextStyle,
navActionTextStyle: navActionTextStyle ?? _navActionTextStyle, navActionTextStyle ?? _navActionTextStyle,
pickerTextStyle: pickerTextStyle ?? _pickerTextStyle, pickerTextStyle ?? _pickerTextStyle,
dateTimePickerTextStyle: dateTimePickerTextStyle ?? _dateTimePickerTextStyle, dateTimePickerTextStyle ?? _dateTimePickerTextStyle,
); );
} }
} }
@immutable
class _DefaultCupertinoTextThemeData extends Diagnosticable {
const _DefaultCupertinoTextThemeData({
this.labelColor = CupertinoColors.label,
this.inactiveGrayColor = CupertinoColors.inactiveGray,
}) : assert(labelColor != null),
assert(inactiveGrayColor != null);
final Color labelColor;
final Color inactiveGrayColor;
static TextStyle _applyLabelColor(TextStyle original, Color color) {
return original?.color == color
? original
: original?.copyWith(color: color);
}
TextStyle get textStyle => _applyLabelColor(_kDefaultTextStyle, labelColor);
TextStyle get tabLabelTextStyle => _applyLabelColor(_kDefaultTabLabelTextStyle, inactiveGrayColor);
TextStyle get navTitleTextStyle => _applyLabelColor(_kDefaultMiddleTitleTextStyle, labelColor);
TextStyle get navLargeTitleTextStyle => _applyLabelColor(_kDefaultLargeTitleTextStyle, labelColor);
TextStyle get pickerTextStyle => _applyLabelColor(_kDefaultPickerTextStyle, labelColor);
TextStyle get dateTimePickerTextStyle => _applyLabelColor(_kDefaultDateTimePickerTextStyle, labelColor);
TextStyle actionTextStyle({ Color primaryColor }) => _kDefaultActionTextStyle.copyWith(color: primaryColor);
TextStyle navActionTextStyle({ Color primaryColor }) => actionTextStyle(primaryColor: primaryColor);
_DefaultCupertinoTextThemeData resolveFrom(BuildContext context, bool nullOk) {
final Color resolvedLabelColor = CupertinoDynamicColor.resolve(labelColor, context, nullOk: nullOk);
final Color resolvedInactiveGray = CupertinoDynamicColor.resolve(inactiveGrayColor, context, nullOk: nullOk);
return resolvedLabelColor == labelColor && resolvedInactiveGray == CupertinoColors.inactiveGray
? this
: _DefaultCupertinoTextThemeData(labelColor: resolvedLabelColor, inactiveGrayColor: resolvedInactiveGray);
}
}
...@@ -7,13 +7,24 @@ import 'package:flutter/services.dart'; ...@@ -7,13 +7,24 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'colors.dart'; import 'colors.dart';
import 'icon_theme_data.dart';
import 'text_theme.dart'; import 'text_theme.dart';
export 'package:flutter/services.dart' show Brightness; export 'package:flutter/services.dart' show Brightness;
// Values derived from https://developer.apple.com/design/resources/. // Values derived from https://developer.apple.com/design/resources/.
const Color _kDefaultBarLightBackgroundColor = Color(0xCCF8F8F8); const _CupertinoThemeDefaults _kDefaultTheme = _CupertinoThemeDefaults(
const Color _kDefaultBarDarkBackgroundColor = Color(0xB7212121); null,
CupertinoColors.systemBlue,
CupertinoColors.systemBackground,
CupertinoDynamicColor.withBrightness(
color: Color(0xF0F9F9F9),
darkColor: Color(0xF01D1D1D),
// Values extracted from navigation bar. For toolbar or tabbar the dark color is 0xF0161616.
),
CupertinoColors.systemBackground,
_CupertinoTextThemeDefaults(CupertinoColors.label, CupertinoColors.inactiveGray),
);
/// Applies a visual styling theme to descendant Cupertino widgets. /// Applies a visual styling theme to descendant Cupertino widgets.
/// ///
...@@ -30,7 +41,8 @@ const Color _kDefaultBarDarkBackgroundColor = Color(0xB7212121); ...@@ -30,7 +41,8 @@ const Color _kDefaultBarDarkBackgroundColor = Color(0xB7212121);
/// See also: /// See also:
/// ///
/// * [CupertinoThemeData], specifies the theme's visual styling. /// * [CupertinoThemeData], specifies the theme's visual styling.
/// * [CupertinoApp], which will automatically add a [CupertinoTheme]. /// * [CupertinoApp], which will automatically add a [CupertinoTheme] based on the
/// value of [CupertinoApp.theme].
/// * [Theme], a Material theme which will automatically add a [CupertinoTheme] /// * [Theme], a Material theme which will automatically add a [CupertinoTheme]
/// with a [CupertinoThemeData] derived from the Material [ThemeData]. /// with a [CupertinoThemeData] derived from the Material [ThemeData].
class CupertinoTheme extends StatelessWidget { class CupertinoTheme extends StatelessWidget {
...@@ -48,20 +60,22 @@ class CupertinoTheme extends StatelessWidget { ...@@ -48,20 +60,22 @@ class CupertinoTheme extends StatelessWidget {
/// The [CupertinoThemeData] styling for this theme. /// The [CupertinoThemeData] styling for this theme.
final CupertinoThemeData data; final CupertinoThemeData data;
/// Retrieve the [CupertinoThemeData] from an ancestor [CupertinoTheme] widget. /// Retrieves the [CupertinoThemeData] from the closest ancestor [CupertinoTheme]
/// widget, or a default [CupertinoThemeData] if no [CupertinoTheme] ancestor
/// exists.
/// ///
/// Returns a default [CupertinoThemeData] if no [CupertinoTheme] widgets /// Resolves all the colors defined in that [CupertinoThemeData] against the
/// exist in the ancestry tree. /// given [BuildContext] on a best-effort basis.
static CupertinoThemeData of(BuildContext context) { static CupertinoThemeData of(BuildContext context) {
final _InheritedCupertinoTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedCupertinoTheme); final _InheritedCupertinoTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedCupertinoTheme);
return (inheritedTheme?.theme?.data ?? const CupertinoThemeData()).resolveFrom(context, nullOk: true); return (inheritedTheme?.theme?.data ?? const CupertinoThemeData()).resolveFrom(context, nullOk: true);
} }
/// Retrieve the [Brightness] value from the closest ancestor [CupertinoTheme] /// Retrieves the [Brightness] value from the closest ancestor [CupertinoTheme]
/// widget. /// widget.
/// ///
/// If no ancestral [CupertinoTheme] widget with explicit brightness value could /// If no [CupertinoTheme] ancestor with an explicit brightness value could be
/// be found, the method will resort to the closest ancestor [MediaQuery] widget. /// found, this method will resort to the closest ancestor [MediaQuery] widget.
/// ///
/// Throws an exception if no such [CupertinoTheme] or [MediaQuery] widgets exist /// Throws an exception if no such [CupertinoTheme] or [MediaQuery] widgets exist
/// in the ancestry tree, unless [nullOk] is set to true. /// in the ancestry tree, unless [nullOk] is set to true.
...@@ -80,7 +94,7 @@ class CupertinoTheme extends StatelessWidget { ...@@ -80,7 +94,7 @@ class CupertinoTheme extends StatelessWidget {
return _InheritedCupertinoTheme( return _InheritedCupertinoTheme(
theme: this, theme: this,
child: IconTheme( child: IconTheme(
data: IconThemeData(color: data.primaryColor), data: CupertinoIconThemeData(color: data.primaryColor),
child: child, child: child,
), ),
); );
...@@ -119,7 +133,7 @@ class _InheritedCupertinoTheme extends InheritedWidget { ...@@ -119,7 +133,7 @@ class _InheritedCupertinoTheme extends InheritedWidget {
/// styling via a [CupertinoThemeData] subclass [MaterialBasedCupertinoThemeData]. /// styling via a [CupertinoThemeData] subclass [MaterialBasedCupertinoThemeData].
@immutable @immutable
class CupertinoThemeData extends Diagnosticable { class CupertinoThemeData extends Diagnosticable {
/// Create a [CupertinoTheme] styling specification. /// Creates a [CupertinoTheme] styling specification.
/// ///
/// Unspecified parameters default to a reasonable iOS default style. /// Unspecified parameters default to a reasonable iOS default style.
const CupertinoThemeData({ const CupertinoThemeData({
...@@ -144,89 +158,104 @@ class CupertinoThemeData extends Diagnosticable { ...@@ -144,89 +158,104 @@ class CupertinoThemeData extends Diagnosticable {
/// Used by subclasses to get the superclass's defaulting behaviors. /// Used by subclasses to get the superclass's defaulting behaviors.
@protected @protected
const CupertinoThemeData.raw( const CupertinoThemeData.raw(
Brightness brightness,
Color primaryColor,
Color primaryContrastingColor,
CupertinoTextThemeData textTheme,
Color barBackgroundColor,
Color scaffoldBackgroundColor,
) : this._rawWithDefaults(
brightness,
primaryColor,
primaryContrastingColor,
textTheme,
barBackgroundColor,
scaffoldBackgroundColor,
_kDefaultTheme,
);
const CupertinoThemeData._rawWithDefaults(
this._brightness, this._brightness,
this._primaryColor, this._primaryColor,
this._primaryContrastingColor, this._primaryContrastingColor,
this._textTheme, this._textTheme,
this._barBackgroundColor, this._barBackgroundColor,
this._scaffoldBackgroundColor, this._scaffoldBackgroundColor,
this._defaults,
); );
bool get _isLight => brightness == Brightness.light; final _CupertinoThemeDefaults _defaults;
/// The general brightness theme of the [CupertinoThemeData]. /// The general brightness theme of the [CupertinoThemeData].
/// ///
/// Affects all other theme properties when unspecified. Defaults to /// Overrides the ambient [MediaQueryData.platformBrightness] when specified.
/// [Brightness.light]. /// Defaults to [Brightness.light].
/// ///
/// If coming from a Material [Theme] and unspecified, [brightness] will be /// If coming from a Material [Theme] and unspecified, [brightness] will be
/// derived from the Material [ThemeData]'s `brightness`. /// derived from the Material [ThemeData]'s `brightness`.
///
/// See also:
///
/// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers
/// [brightness] to its Material [Theme] parent if it's unspecified.
Brightness get brightness => _brightness ?? Brightness.light; Brightness get brightness => _brightness ?? Brightness.light;
final Brightness _brightness; final Brightness _brightness;
/// A color used on interactive elements of the theme. /// A color used on interactive elements of the theme.
/// ///
/// This color is generally used on text and icons in buttons and tappable /// This color is generally used on text and icons in buttons and tappable
/// elements. Defaults to [CupertinoColors.activeBlue] or /// elements. Defaults to [CupertinoColors.activeBlue].
/// [CupertinoColors.activeOrange] when [brightness] is light or dark.
/// ///
/// If coming from a Material [Theme] and unspecified, [primaryColor] will be /// If coming from a Material [Theme] and unspecified, [primaryColor] will be
/// derived from the Material [ThemeData]'s `colorScheme.primary`. However, in /// derived from the Material [ThemeData]'s `colorScheme.primary`. However, in
/// iOS styling, the [primaryColor] is more sparsely used than in Material /// iOS styling, the [primaryColor] is more sparsely used than in Material
/// Design where the [primaryColor] can appear on non-interactive surfaces like /// Design where the [primaryColor] can appear on non-interactive surfaces like
/// the [AppBar] background, [TextField] borders etc. /// the [AppBar] background, [TextField] borders etc.
Color get primaryColor { ///
return _primaryColor ?? /// See also:
(_isLight ? CupertinoColors.activeBlue : CupertinoColors.activeOrange); ///
} /// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers
/// [primaryColor] to its Material [Theme] parent if it's unspecified.
Color get primaryColor => _primaryColor ?? _defaults.primaryColor;
final Color _primaryColor; final Color _primaryColor;
/// A color used for content that must contrast against a [primaryColor] background. /// A color that must be easy to see when rendered on a [primaryColor] background.
/// ///
/// For example, this color is used for a [CupertinoButton]'s text and icons /// For example, this color is used for a [CupertinoButton]'s text and icons
/// when the button's background is [primaryColor]. /// when the button's background is [primaryColor].
/// ///
/// If coming from a Material [Theme] and unspecified, [primaryContrastingColor] /// If coming from a Material [Theme] and unspecified, [primaryContrastingColor]
/// will be derived from the Material [ThemeData]'s `colorScheme.onPrimary`. /// will be derived from the Material [ThemeData]'s `colorScheme.onPrimary`.
Color get primaryContrastingColor { ///
return _primaryContrastingColor ?? /// See also:
(_isLight ? CupertinoColors.white : CupertinoColors.black); ///
} /// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers
/// [primaryContrastingColor] to its Material [Theme] parent if it's unspecified.
Color get primaryContrastingColor => _primaryContrastingColor ?? _defaults.primaryContrastingColor;
final Color _primaryContrastingColor; final Color _primaryContrastingColor;
/// Text styles used by Cupertino widgets. /// Text styles used by Cupertino widgets.
/// ///
/// Derived from [brightness] and [primaryColor] if unspecified, including /// Derived from [primaryColor] if unspecified.
/// [brightness] and [primaryColor] of a Material [ThemeData] if coming
/// from a Material [Theme].
CupertinoTextThemeData get textTheme { CupertinoTextThemeData get textTheme {
return _textTheme ?? CupertinoTextThemeData( return _textTheme ?? _defaults.textThemeDefaults.createDefaults(primaryColor: primaryColor);
brightness: brightness,
primaryColor: primaryColor,
);
} }
final CupertinoTextThemeData _textTheme; final CupertinoTextThemeData _textTheme;
/// Background color of the top nav bar and bottom tab bar. /// Background color of the top nav bar and bottom tab bar.
/// ///
/// Defaults to a light gray or a dark gray translucent color depending /// Defaults to a light gray in light mode, or a dark translucent gray color in
/// on the [brightness]. /// dark mode.
Color get barBackgroundColor { Color get barBackgroundColor => _barBackgroundColor ?? _defaults.barBackgroundColor;
return _barBackgroundColor ??
(_isLight ? _kDefaultBarLightBackgroundColor : _kDefaultBarDarkBackgroundColor);
}
final Color _barBackgroundColor; final Color _barBackgroundColor;
/// Background color of the scaffold. /// Background color of the scaffold.
/// ///
/// Defaults to white or black depending on the [brightness]. /// Defaults to [CupertinoColors.systemBackground].
Color get scaffoldBackgroundColor { Color get scaffoldBackgroundColor => _scaffoldBackgroundColor ?? _defaults.scaffoldBackgroundColor;
return _scaffoldBackgroundColor ??
(_isLight ? CupertinoColors.white : CupertinoColors.black);
}
final Color _scaffoldBackgroundColor; final Color _scaffoldBackgroundColor;
/// Return an instance of the [CupertinoThemeData] whose property getters /// Returns an instance of the [CupertinoThemeData] whose property getters
/// only return the construction time specifications with no derived values. /// only return the construction time specifications with no derived values.
/// ///
/// Used in Material themes to let unspecified properties fallback to Material /// Used in Material themes to let unspecified properties fallback to Material
...@@ -242,30 +271,33 @@ class CupertinoThemeData extends Diagnosticable { ...@@ -242,30 +271,33 @@ class CupertinoThemeData extends Diagnosticable {
); );
} }
/// Return a new `CupertinoThemeData` whose colors are from this `CupertinoThemeData`, /// Returns a new `CupertinoThemeData` with all its colors resolved aginst the
/// but resolved aginst the given [BuildContext]. /// given [BuildContext].
/// ///
/// It will be called in [CupertinoTheme.of]. /// Called by [CupertinoTheme.of] to resolve colors defined in the retrieved
/// [CupertinoThemeData].
@protected @protected
CupertinoThemeData resolveFrom(BuildContext context, { bool nullOk = false }) { CupertinoThemeData resolveFrom(BuildContext context, { bool nullOk = false }) {
Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context, nullOk: nullOk); Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context, nullOk: nullOk);
return copyWith( return CupertinoThemeData._rawWithDefaults(
primaryColor: convertColor(primaryColor), _brightness,
primaryContrastingColor: convertColor(primaryContrastingColor), convertColor(_primaryColor),
textTheme: textTheme?.resolveFrom(context, nullOk: nullOk), convertColor(_primaryContrastingColor),
barBackgroundColor: convertColor(barBackgroundColor), textTheme?.resolveFrom(context, nullOk: nullOk),
scaffoldBackgroundColor: convertColor(scaffoldBackgroundColor), convertColor(_barBackgroundColor),
convertColor(_scaffoldBackgroundColor),
_defaults.resolveFrom(context, nullOk: nullOk),
); );
} }
/// Create a copy of [CupertinoThemeData] with specified attributes overridden. /// Creates a copy of [CupertinoThemeData] with specified attributes overridden.
/// ///
/// Only the current instance's specified attributes are copied instead of /// Only the current instance's specified attributes are copied instead of
/// derived values. For instance, if the current [primaryColor] is implied /// derived values. For instance, if the current [CupertinoThemeData.textTheme]
/// to be [CupertinoColors.activeOrange] due to the current [brightness], /// is implied from the current [primaryColor] because it was not specified,
/// copying with a different [brightness] will also change the copy's /// copying with a different [primaryColor] will also change the copy's implied
/// implied [primaryColor]. /// [textTheme].
CupertinoThemeData copyWith({ CupertinoThemeData copyWith({
Brightness brightness, Brightness brightness,
Color primaryColor, Color primaryColor,
...@@ -274,13 +306,14 @@ class CupertinoThemeData extends Diagnosticable { ...@@ -274,13 +306,14 @@ class CupertinoThemeData extends Diagnosticable {
Color barBackgroundColor, Color barBackgroundColor,
Color scaffoldBackgroundColor, Color scaffoldBackgroundColor,
}) { }) {
return CupertinoThemeData( return CupertinoThemeData._rawWithDefaults(
brightness: brightness ?? _brightness, brightness ?? _brightness,
primaryColor: primaryColor ?? _primaryColor, primaryColor ?? _primaryColor,
primaryContrastingColor: primaryContrastingColor ?? _primaryContrastingColor, primaryContrastingColor ?? _primaryContrastingColor,
textTheme: textTheme ?? _textTheme, textTheme ?? _textTheme,
barBackgroundColor: barBackgroundColor ?? _barBackgroundColor, barBackgroundColor ?? _barBackgroundColor,
scaffoldBackgroundColor: scaffoldBackgroundColor ?? _scaffoldBackgroundColor, scaffoldBackgroundColor ?? _scaffoldBackgroundColor,
_defaults,
); );
} }
...@@ -305,13 +338,14 @@ class _NoDefaultCupertinoThemeData extends CupertinoThemeData { ...@@ -305,13 +338,14 @@ class _NoDefaultCupertinoThemeData extends CupertinoThemeData {
this.textTheme, this.textTheme,
this.barBackgroundColor, this.barBackgroundColor,
this.scaffoldBackgroundColor, this.scaffoldBackgroundColor,
) : super.raw( ) : super._rawWithDefaults(
brightness, brightness,
primaryColor, primaryColor,
primaryContrastingColor, primaryContrastingColor,
textTheme, textTheme,
barBackgroundColor, barBackgroundColor,
scaffoldBackgroundColor, scaffoldBackgroundColor,
null,
); );
@override @override
...@@ -360,3 +394,98 @@ class _NoDefaultCupertinoThemeData extends CupertinoThemeData { ...@@ -360,3 +394,98 @@ class _NoDefaultCupertinoThemeData extends CupertinoThemeData {
); );
} }
} }
@immutable
class _CupertinoThemeDefaults {
const _CupertinoThemeDefaults(
this.brightness,
this.primaryColor,
this.primaryContrastingColor,
this.barBackgroundColor,
this.scaffoldBackgroundColor,
this.textThemeDefaults,
);
final Brightness brightness;
final Color primaryColor;
final Color primaryContrastingColor;
final Color barBackgroundColor;
final Color scaffoldBackgroundColor;
final _CupertinoTextThemeDefaults textThemeDefaults;
_CupertinoThemeDefaults resolveFrom(BuildContext context, { @required bool nullOk }) {
assert(nullOk != null);
Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context, nullOk: nullOk);
return _CupertinoThemeDefaults(
brightness,
convertColor(primaryColor),
convertColor(primaryContrastingColor),
convertColor(barBackgroundColor),
convertColor(scaffoldBackgroundColor),
textThemeDefaults?.resolveFrom(context, nullOk: nullOk),
);
}
}
@immutable
class _CupertinoTextThemeDefaults {
const _CupertinoTextThemeDefaults(
this.labelColor,
this.inactiveGray,
);
final Color labelColor;
final Color inactiveGray;
_CupertinoTextThemeDefaults resolveFrom(BuildContext context, { @required bool nullOk }) {
return _CupertinoTextThemeDefaults(
CupertinoDynamicColor.resolve(labelColor, context, nullOk: nullOk),
CupertinoDynamicColor.resolve(inactiveGray, context, nullOk: nullOk),
);
}
CupertinoTextThemeData createDefaults({ @required Color primaryColor }) {
assert(primaryColor != null);
return _DefaultCupertinoTextThemeData(
primaryColor: primaryColor,
labelColor: labelColor,
inactiveGray: inactiveGray,
);
}
}
// CupertinoTextThemeData with no text styles explicitly specified.
// The implementation of this class may need to be updated when any of the default
// text styles changes.
class _DefaultCupertinoTextThemeData extends CupertinoTextThemeData {
const _DefaultCupertinoTextThemeData({
@required this.labelColor,
@required this.inactiveGray,
@required Color primaryColor,
}) : assert(labelColor != null),
assert(inactiveGray != null),
assert(primaryColor != null),
super(primaryColor: primaryColor);
final Color labelColor;
final Color inactiveGray;
@override
TextStyle get textStyle => super.textStyle.copyWith(color: labelColor);
@override
TextStyle get tabLabelTextStyle => super.tabLabelTextStyle.copyWith(color: inactiveGray);
@override
TextStyle get navTitleTextStyle => super.navTitleTextStyle.copyWith(color: labelColor);
@override
TextStyle get navLargeTitleTextStyle => super.navLargeTitleTextStyle.copyWith(color: labelColor);
@override
TextStyle get pickerTextStyle => super.pickerTextStyle.copyWith(color: labelColor);
@override
TextStyle get dateTimePickerTextStyle => super.dateTimePickerTextStyle.copyWith(color: labelColor);
}
...@@ -59,7 +59,7 @@ class IconTheme extends InheritedTheme { ...@@ -59,7 +59,7 @@ class IconTheme extends InheritedTheme {
/// IconThemeData theme = IconTheme.of(context); /// IconThemeData theme = IconTheme.of(context);
/// ``` /// ```
static IconThemeData of(BuildContext context) { static IconThemeData of(BuildContext context) {
final IconThemeData iconThemeData = _getInheritedIconThemeData(context); final IconThemeData iconThemeData = _getInheritedIconThemeData(context).resolve(context);
return iconThemeData.isConcrete ? iconThemeData : const IconThemeData.fallback().merge(iconThemeData); return iconThemeData.isConcrete ? iconThemeData : const IconThemeData.fallback().merge(iconThemeData);
} }
......
...@@ -8,6 +8,8 @@ import 'dart:ui' as ui show lerpDouble; ...@@ -8,6 +8,8 @@ import 'dart:ui' as ui show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
import 'framework.dart' show BuildContext;
/// Defines the color, opacity, and size of icons. /// Defines the color, opacity, and size of icons.
/// ///
/// Used by [IconTheme] to control the color, opacity, and size of icons in a /// Used by [IconTheme] to control the color, opacity, and size of icons in a
...@@ -54,6 +56,24 @@ class IconThemeData extends Diagnosticable { ...@@ -54,6 +56,24 @@ class IconThemeData extends Diagnosticable {
); );
} }
/// Called by [IconTheme.of] to convert this instance to an [IconThemeData]
/// that fits the given [BuildContext].
///
/// This method gives the ambient [IconThemeData] a chance to update itself,
/// after it's been retrieved by [IconTheme.of], and before being returned as
/// the final result. For instance, [CupertinoIconThemeData] overrides this method
/// to resolve [color], in case [color] is a [CupertinoDynamicColor] and needs
/// to be resolved against the given [BuildContext] before it can be used as a
/// regular [Color].
///
/// The default implementation returns this [IconThemeData] as-is.
///
/// See also:
///
/// * [CupertinoIconThemeData.resolve] an implementation that resolves
/// [CupertinoIconThemeData.color] before returning.
IconThemeData resolve(BuildContext context) => this;
/// Whether all the properties of this object are non-null. /// Whether all the properties of this object are non-null.
bool get isConcrete => color != null && opacity != null && size != null; bool get isConcrete => color != null && opacity != null && size != null;
......
...@@ -218,7 +218,7 @@ void main() { ...@@ -218,7 +218,7 @@ void main() {
matching: find.byType(RichText), matching: find.byType(RichText),
)); ));
expect(actualActive.text.style.color.value, CupertinoColors.activeOrange.darkColor.value); expect(actualActive.text.style.color, isSameColorAs(CupertinoColors.activeBlue.darkColor));
}); });
testWidgets('Use active icon', (WidgetTester tester) async { testWidgets('Use active icon', (WidgetTester tester) async {
......
...@@ -306,7 +306,7 @@ void main() { ...@@ -306,7 +306,7 @@ void main() {
), ),
); );
expect(textStyle.color, CupertinoColors.white); expect(textStyle.color, isSameColorAs(CupertinoColors.white));
BoxDecoration decoration = tester.widget<DecoratedBox>( BoxDecoration decoration = tester.widget<DecoratedBox>(
find.descendant( find.descendant(
of: find.byType(CupertinoButton), of: find.byType(CupertinoButton),
...@@ -327,7 +327,7 @@ void main() { ...@@ -327,7 +327,7 @@ void main() {
), ),
), ),
); );
expect(textStyle.color.value, CupertinoColors.activeOrange.darkColor.value); expect(textStyle.color, isSameColorAs(CupertinoColors.systemBlue.darkColor));
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
...@@ -341,14 +341,14 @@ void main() { ...@@ -341,14 +341,14 @@ void main() {
), ),
), ),
); );
expect(textStyle.color, CupertinoColors.black); expect(textStyle.color, isSameColorAs(CupertinoColors.black));
decoration = tester.widget<DecoratedBox>( decoration = tester.widget<DecoratedBox>(
find.descendant( find.descendant(
of: find.byType(CupertinoButton), of: find.byType(CupertinoButton),
matching: find.byType(DecoratedBox), matching: find.byType(DecoratedBox),
), ),
).decoration; ).decoration;
expect(decoration.color.value, CupertinoColors.activeOrange.darkColor.value); expect(decoration.color, isSameColorAs(CupertinoColors.systemBlue.darkColor));
}); });
} }
......
// Copyright 2019 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.
import 'package:flutter/cupertino.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('IconTheme.of works', (WidgetTester tester) async {
const IconThemeData data = IconThemeData(color: Color(0xAAAAAAAA), opacity: 0.5, size: 16.0);
IconThemeData retrieved;
await tester.pumpWidget(
IconTheme(data: data, child: Builder(builder: (BuildContext context) {
retrieved = IconTheme.of(context);
return const SizedBox();
}))
);
expect(retrieved, data);
await tester.pumpWidget(
IconTheme(
data: const CupertinoIconThemeData(color: CupertinoColors.systemBlue),
child: MediaQuery(
data: const MediaQueryData(platformBrightness: Brightness.dark),
child: Builder(builder: (BuildContext context) {
retrieved = IconTheme.of(context);
return const SizedBox();
},
)
),
),
);
expect(retrieved.color, isSameColorAs(CupertinoColors.systemBlue.darkColor));
});
}
...@@ -133,7 +133,7 @@ void main() { ...@@ -133,7 +133,7 @@ void main() {
matching: find.byType(DecoratedBox), matching: find.byType(DecoratedBox),
)).decoration; )).decoration;
expect(tabDecoration.color, const Color(0xCCF8F8F8)); expect(tabDecoration.color, isSameColorAs(const Color(0xF0F9F9F9))); // Inherited from theme.
await tester.tap(find.text('Tab 2')); await tester.tap(find.text('Tab 2'));
await tester.pump(); await tester.pump();
...@@ -159,7 +159,7 @@ void main() { ...@@ -159,7 +159,7 @@ void main() {
matching: find.byType(DecoratedBox), matching: find.byType(DecoratedBox),
)).decoration; )).decoration;
expect(tabDecoration.color, const Color(0xB7212121)); expect(tabDecoration.color, isSameColorAs(const Color(0xF01D1D1D)));
final RichText tab1 = tester.widget(find.descendant( final RichText tab1 = tester.widget(find.descendant(
of: find.text('Tab 1'), of: find.text('Tab 1'),
......
...@@ -188,19 +188,18 @@ void main() { ...@@ -188,19 +188,18 @@ void main() {
testWidgets('Nav bar respects themes', (WidgetTester tester) async { testWidgets('Nav bar respects themes', (WidgetTester tester) async {
count = 0x000000; count = 0x000000;
const CupertinoDynamicColor orange = CupertinoColors.activeOrange;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.dark), theme: const CupertinoThemeData(brightness: Brightness.dark),
home: CupertinoNavigationBar( home: CupertinoNavigationBar(
leading: CupertinoButton( leading: CupertinoButton(
onPressed: () { }, onPressed: () { },
child: _ExpectStyles(color: orange.darkColor, index: 0x000001), child: _ExpectStyles(color: CupertinoColors.systemBlue.darkColor, index: 0x000001),
), ),
middle: const _ExpectStyles(color: CupertinoColors.white, index: 0x000100), middle: const _ExpectStyles(color: CupertinoColors.white, index: 0x000100),
trailing: CupertinoButton( trailing: CupertinoButton(
onPressed: () { }, onPressed: () { },
child: _ExpectStyles(color: orange.darkColor, index: 0x010000), child: _ExpectStyles(color: CupertinoColors.systemBlue.darkColor, index: 0x010000),
), ),
), ),
), ),
...@@ -1129,7 +1128,7 @@ class _ExpectStyles extends StatelessWidget { ...@@ -1129,7 +1128,7 @@ class _ExpectStyles extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TextStyle style = DefaultTextStyle.of(context).style; final TextStyle style = DefaultTextStyle.of(context).style;
expect(style.color.value, color.value); expect(style.color, isSameColorAs(color));
expect(style.fontFamily, '.SF Pro Text'); expect(style.fontFamily, '.SF Pro Text');
expect(style.fontSize, 17.0); expect(style.fontSize, 17.0);
expect(style.letterSpacing, -0.41); expect(style.letterSpacing, -0.41);
......
...@@ -226,7 +226,7 @@ void main() { ...@@ -226,7 +226,7 @@ void main() {
// The transition's stack is ordered. The bottom middle is inserted first. // The transition's stack is ordered. The bottom middle is inserted first.
final RenderParagraph bottomMiddle = final RenderParagraph bottomMiddle =
tester.renderObject(flying(tester, find.text('Page 1')).first); tester.renderObject(flying(tester, find.text('Page 1')).first);
expect(bottomMiddle.text.style.color, const Color(0xfffffaf4)); expect(bottomMiddle.text.style.color, const Color(0xFFF4F9FF));
expect(bottomMiddle.text.style.fontWeight, FontWeight.w600); expect(bottomMiddle.text.style.fontWeight, FontWeight.w600);
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text'); expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
expect(bottomMiddle.text.style.letterSpacing, -0.41); expect(bottomMiddle.text.style.letterSpacing, -0.41);
...@@ -237,7 +237,7 @@ void main() { ...@@ -237,7 +237,7 @@ void main() {
// are flipped. // are flipped.
final RenderParagraph topBackLabel = final RenderParagraph topBackLabel =
tester.renderObject(flying(tester, find.text('Page 1')).last); tester.renderObject(flying(tester, find.text('Page 1')).last);
expect(topBackLabel.text.style.color, const Color(0xfffffaf4)); expect(topBackLabel.text.style.color, const Color(0xFFF4F9FF));
expect(topBackLabel.text.style.fontWeight, FontWeight.w600); expect(topBackLabel.text.style.fontWeight, FontWeight.w600);
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text'); expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
expect(topBackLabel.text.style.letterSpacing, -0.41); expect(topBackLabel.text.style.letterSpacing, -0.41);
...@@ -246,14 +246,14 @@ void main() { ...@@ -246,14 +246,14 @@ void main() {
// Move animation further a bit. // Move animation further a bit.
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
expect(bottomMiddle.text.style.color, const Color(0xffffa923)); expect(bottomMiddle.text.style.color, const Color(0xFF2390FF));
expect(bottomMiddle.text.style.fontWeight, FontWeight.w400); expect(bottomMiddle.text.style.fontWeight, FontWeight.w400);
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text'); expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
expect(bottomMiddle.text.style.letterSpacing, -0.41); expect(bottomMiddle.text.style.letterSpacing, -0.41);
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0); checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0);
expect(topBackLabel.text.style.color, const Color(0xffffa923)); expect(topBackLabel.text.style.color, const Color(0xFF2390FF));
expect(topBackLabel.text.style.fontWeight, FontWeight.w400); expect(topBackLabel.text.style.fontWeight, FontWeight.w400);
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text'); expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
expect(topBackLabel.text.style.letterSpacing, -0.41); expect(topBackLabel.text.style.letterSpacing, -0.41);
......
...@@ -34,10 +34,11 @@ void main() { ...@@ -34,10 +34,11 @@ void main() {
final RenderParagraph paragraph = tester.renderObject(find.text('1')); final RenderParagraph paragraph = tester.renderObject(find.text('1'));
expect(paragraph.text.style, const TextStyle( expect(paragraph.text.style.color, isSameColorAs(CupertinoColors.black));
expect(paragraph.text.style.copyWith(color: CupertinoColors.black), const TextStyle(
inherit: false, inherit: false,
fontFamily: '.SF Pro Display', fontFamily: '.SF Pro Display',
fontSize: 25.0, fontSize: 21.0,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
letterSpacing: -0.41, letterSpacing: -0.41,
color: CupertinoColors.black, color: CupertinoColors.black,
......
...@@ -411,7 +411,7 @@ void main() { ...@@ -411,7 +411,7 @@ void main() {
expect(decoratedBox.decoration.runtimeType, BoxDecoration); expect(decoratedBox.decoration.runtimeType, BoxDecoration);
final BoxDecoration decoration = decoratedBox.decoration; final BoxDecoration decoration = decoratedBox.decoration;
expect(decoration.color, CupertinoColors.white); expect(decoration.color, isSameColorAs(CupertinoColors.white));
}); });
testWidgets('Overrides background color', (WidgetTester tester) async { testWidgets('Overrides background color', (WidgetTester tester) async {
......
...@@ -290,7 +290,7 @@ void main() { ...@@ -290,7 +290,7 @@ void main() {
DefaultTextStyle textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1')); DefaultTextStyle textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1'));
IconTheme iconTheme = tester.widget(find.widgetWithIcon(IconTheme, const IconData(1))); IconTheme iconTheme = tester.widget(find.widgetWithIcon(IconTheme, const IconData(1)));
expect(textStyle.style.color, CupertinoColors.white); expect(textStyle.style.color, isSameColorAs(CupertinoColors.white));
expect(iconTheme.data.color, CupertinoColors.activeBlue); expect(iconTheme.data.color, CupertinoColors.activeBlue);
await tester.tap(find.widgetWithIcon(IconTheme, const IconData(1))); await tester.tap(find.widgetWithIcon(IconTheme, const IconData(1)));
...@@ -300,7 +300,7 @@ void main() { ...@@ -300,7 +300,7 @@ void main() {
iconTheme = tester.widget(find.widgetWithIcon(IconTheme, const IconData(1))); iconTheme = tester.widget(find.widgetWithIcon(IconTheme, const IconData(1)));
expect(textStyle.style.color, CupertinoColors.activeBlue); expect(textStyle.style.color, CupertinoColors.activeBlue);
expect(iconTheme.data.color, CupertinoColors.white); expect(iconTheme.data.color, isSameColorAs(CupertinoColors.white));
}); });
testWidgets( testWidgets(
...@@ -334,8 +334,8 @@ void main() { ...@@ -334,8 +334,8 @@ void main() {
DefaultTextStyle textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first); DefaultTextStyle textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first);
IconThemeData iconTheme = IconTheme.of(tester.element(find.byIcon(const IconData(1)))); IconThemeData iconTheme = IconTheme.of(tester.element(find.byIcon(const IconData(1))));
expect(textStyle.style.color, CupertinoColors.black); expect(textStyle.style.color, isSameColorAs(CupertinoColors.black));
expect(iconTheme.color.value, CupertinoColors.activeOrange.darkColor.value); expect(iconTheme.color, isSameColorAs(CupertinoColors.systemBlue.darkColor));
await tester.tap(find.byIcon(const IconData(1))); await tester.tap(find.byIcon(const IconData(1)));
await tester.pump(); await tester.pump();
...@@ -344,8 +344,8 @@ void main() { ...@@ -344,8 +344,8 @@ void main() {
textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first); textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first);
iconTheme = IconTheme.of(tester.element(find.byIcon(const IconData(1)))); iconTheme = IconTheme.of(tester.element(find.byIcon(const IconData(1))));
expect(textStyle.style.color.value, CupertinoColors.activeOrange.darkColor.value); expect(textStyle.style.color, isSameColorAs(CupertinoColors.systemBlue.darkColor));
expect(iconTheme.color, CupertinoColors.black); expect(iconTheme.color, isSameColorAs(CupertinoColors.black));
}, },
); );
...@@ -484,13 +484,13 @@ void main() { ...@@ -484,13 +484,13 @@ void main() {
); );
expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
await tester.tap(find.text('Child 2')); await tester.tap(find.text('Child 2'));
await tester.pump(); await tester.pump();
expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
}); });
testWidgets( testWidgets(
...@@ -499,7 +499,7 @@ void main() { ...@@ -499,7 +499,7 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(setupSimpleSegmentedControl()); await tester.pumpWidget(setupSimpleSegmentedControl());
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
await tester.tap(find.text('Child 2')); await tester.tap(find.text('Child 2'));
await tester.pumpAndSettle(const Duration(milliseconds: 200)); await tester.pumpAndSettle(const Duration(milliseconds: 200));
...@@ -552,7 +552,7 @@ void main() { ...@@ -552,7 +552,7 @@ void main() {
expect(getRenderSegmentedControl(tester).selectedIndex, 0); expect(getRenderSegmentedControl(tester).selectedIndex, 0);
expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
}); });
testWidgets('Null input for value results in no child initially selected', (WidgetTester tester) async { testWidgets('Null input for value results in no child initially selected', (WidgetTester tester) async {
...@@ -582,15 +582,15 @@ void main() { ...@@ -582,15 +582,15 @@ void main() {
expect(getRenderSegmentedControl(tester).selectedIndex, null); expect(getRenderSegmentedControl(tester).selectedIndex, null);
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
}); });
testWidgets('Long press changes background color of not-selected child', (WidgetTester tester) async { testWidgets('Long press changes background color of not-selected child', (WidgetTester tester) async {
await tester.pumpWidget(setupSimpleSegmentedControl()); await tester.pumpWidget(setupSimpleSegmentedControl());
expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
final Offset center = tester.getCenter(find.text('Child 2')); final Offset center = tester.getCenter(find.text('Child 2'));
await tester.startGesture(center); await tester.startGesture(center);
...@@ -604,14 +604,14 @@ void main() { ...@@ -604,14 +604,14 @@ void main() {
await tester.pumpWidget(setupSimpleSegmentedControl()); await tester.pumpWidget(setupSimpleSegmentedControl());
expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
final Offset center = tester.getCenter(find.text('Child 1')); final Offset center = tester.getCenter(find.text('Child 1'));
await tester.startGesture(center); await tester.startGesture(center);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
}); });
testWidgets('Height of segmented control is determined by tallest widget', (WidgetTester tester) async { testWidgets('Height of segmented control is determined by tallest widget', (WidgetTester tester) async {
...@@ -768,12 +768,12 @@ void main() { ...@@ -768,12 +768,12 @@ void main() {
); );
expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
await tester.tap(find.text('Child 2')); await tester.tap(find.text('Child 2'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue);
await tester.tap(find.text('Child 2')); await tester.tap(find.text('Child 2'));
...@@ -952,7 +952,7 @@ void main() { ...@@ -952,7 +952,7 @@ void main() {
expect(getBackgroundColor(tester, 1), const Color(0xf8007aff)); expect(getBackgroundColor(tester, 1), const Color(0xf8007aff));
await tester.pump(const Duration(milliseconds: 40)); await tester.pump(const Duration(milliseconds: 40));
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue);
}); });
...@@ -1104,7 +1104,7 @@ void main() { ...@@ -1104,7 +1104,7 @@ void main() {
), ),
const Duration(milliseconds: 40), const Duration(milliseconds: 40),
); );
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue);
}); });
...@@ -1134,20 +1134,20 @@ void main() { ...@@ -1134,20 +1134,20 @@ void main() {
), ),
); );
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
await tester.startGesture(tester.getCenter(find.text('B'))); await tester.startGesture(tester.getCenter(find.text('B')));
await tester.pumpAndSettle(const Duration(milliseconds: 200)); await tester.pumpAndSettle(const Duration(milliseconds: 200));
expect(getBackgroundColor(tester, 1), const Color(0x33007aff)); expect(getBackgroundColor(tester, 1), const Color(0x33007aff));
expect(getBackgroundColor(tester, 2), CupertinoColors.white); expect(getBackgroundColor(tester, 2), isSameColorAs(CupertinoColors.white));
await tester.startGesture(tester.getCenter(find.text('C'))); await tester.startGesture(tester.getCenter(find.text('C')));
await tester.pumpAndSettle(const Duration(milliseconds: 200)); await tester.pumpAndSettle(const Duration(milliseconds: 200));
// Press on C has no effect while B is held down. // Press on C has no effect while B is held down.
expect(getBackgroundColor(tester, 1), const Color(0x33007aff)); expect(getBackgroundColor(tester, 1), const Color(0x33007aff));
expect(getBackgroundColor(tester, 2), CupertinoColors.white); expect(getBackgroundColor(tester, 2), isSameColorAs(CupertinoColors.white));
}); });
testWidgets('Transition is triggered while a transition is already occurring', (WidgetTester tester) async { testWidgets('Transition is triggered while a transition is already occurring', (WidgetTester tester) async {
...@@ -1199,12 +1199,12 @@ void main() { ...@@ -1199,12 +1199,12 @@ void main() {
await tester.pump(const Duration(milliseconds: 40)); await tester.pump(const Duration(milliseconds: 40));
// B background color has reached unselected state. // B background color has reached unselected state.
expect(getBackgroundColor(tester, 0), const Color(0xff7bbaff)); expect(getBackgroundColor(tester, 0), const Color(0xff7bbaff));
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 2), const Color(0x64007aff)); expect(getBackgroundColor(tester, 2), const Color(0x64007aff));
await tester.pump(const Duration(milliseconds: 100)); await tester.pump(const Duration(milliseconds: 100));
// A background color has reached unselected state. // A background color has reached unselected state.
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 2), const Color(0xe0007aff)); expect(getBackgroundColor(tester, 2), const Color(0xe0007aff));
await tester.pump(const Duration(milliseconds: 40)); await tester.pump(const Duration(milliseconds: 40));
...@@ -1237,7 +1237,7 @@ void main() { ...@@ -1237,7 +1237,7 @@ void main() {
await tester.pump(const Duration(milliseconds: 40)); await tester.pump(const Duration(milliseconds: 40));
// A and B finish transitioning. // A and B finish transitioning.
expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 0), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
}); });
testWidgets('Add segment while animation is running', (WidgetTester tester) async { testWidgets('Add segment while animation is running', (WidgetTester tester) async {
...@@ -1273,19 +1273,19 @@ void main() { ...@@ -1273,19 +1273,19 @@ void main() {
await tester.tap(find.text('B')); await tester.tap(find.text('B'));
await tester.pump(); await tester.pump();
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 3), CupertinoColors.white); expect(getBackgroundColor(tester, 3), isSameColorAs(CupertinoColors.white));
await tester.pump(const Duration(milliseconds: 40)); await tester.pump(const Duration(milliseconds: 40));
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 3), CupertinoColors.white); expect(getBackgroundColor(tester, 3), isSameColorAs(CupertinoColors.white));
await tester.pump(const Duration(milliseconds: 150)); await tester.pump(const Duration(milliseconds: 150));
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue); expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue);
expect(getBackgroundColor(tester, 3), CupertinoColors.white); expect(getBackgroundColor(tester, 3), isSameColorAs(CupertinoColors.white));
}); });
testWidgets('Remove segment while animation is running', (WidgetTester tester) async { testWidgets('Remove segment while animation is running', (WidgetTester tester) async {
...@@ -1373,15 +1373,15 @@ void main() { ...@@ -1373,15 +1373,15 @@ void main() {
await tester.pump(const Duration(milliseconds: 40)); await tester.pump(const Duration(milliseconds: 40));
expect(getBackgroundColor(tester, 0), const Color(0xff3d9aff)); expect(getBackgroundColor(tester, 0), const Color(0xff3d9aff));
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
await tester.pump(const Duration(milliseconds: 40)); await tester.pump(const Duration(milliseconds: 40));
expect(getBackgroundColor(tester, 0), const Color(0xff7bbaff)); expect(getBackgroundColor(tester, 0), const Color(0xff7bbaff));
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
await tester.pump(const Duration(milliseconds: 100)); await tester.pump(const Duration(milliseconds: 100));
expect(getBackgroundColor(tester, 0), CupertinoColors.white); expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white));
expect(getBackgroundColor(tester, 1), CupertinoColors.white); expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white));
}); });
testWidgets('Golden Test Placeholder Widget', (WidgetTester tester) async { testWidgets('Golden Test Placeholder Widget', (WidgetTester tester) async {
......
...@@ -427,10 +427,9 @@ void main() { ...@@ -427,10 +427,9 @@ void main() {
), ),
); );
const CupertinoDynamicColor orange = CupertinoColors.activeOrange;
expect( expect(
find.byType(CupertinoSlider), find.byType(CupertinoSlider),
paints..rrect(color: orange.darkColor), paints..rrect(color: CupertinoColors.systemBlue.darkColor),
); );
}); });
......
...@@ -340,7 +340,7 @@ void main() { ...@@ -340,7 +340,7 @@ void main() {
matching: find.byType(DecoratedBox), matching: find.byType(DecoratedBox),
)).decoration; )).decoration;
expect(tabDecoration.color, const Color(0xCCF8F8F8)); expect(tabDecoration.color, isSameColorAs(const Color(0xF0F9F9F9))); // Inherited from theme.
await tester.tap(find.text('Tab 2')); await tester.tap(find.text('Tab 2'));
await tester.pump(); await tester.pump();
...@@ -366,7 +366,7 @@ void main() { ...@@ -366,7 +366,7 @@ void main() {
matching: find.byType(DecoratedBox), matching: find.byType(DecoratedBox),
)).decoration; )).decoration;
expect(tabDecoration.color, const Color(0xB7212121)); expect(tabDecoration.color, isSameColorAs(const Color(0xF01D1D1D)));
final RichText tab1 = tester.widget(find.descendant( final RichText tab1 = tester.widget(find.descendant(
of: find.text('Tab 1'), of: find.text('Tab 1'),
...@@ -378,7 +378,7 @@ void main() { ...@@ -378,7 +378,7 @@ void main() {
of: find.text('Tab 2'), of: find.text('Tab 2'),
matching: find.byType(RichText), matching: find.byType(RichText),
)); ));
expect(tab2.text.style.color.value, CupertinoColors.systemRed.darkColor.value); expect(tab2.text.style.color, isSameColorAs(CupertinoColors.systemRed.darkColor));
}); });
testWidgets('Tab contents are padded when there are view insets', (WidgetTester tester) async { testWidgets('Tab contents are padded when there are view insets', (WidgetTester tester) async {
......
...@@ -2749,7 +2749,7 @@ void main() { ...@@ -2749,7 +2749,7 @@ void main() {
tester.renderObject<RenderEditable>( tester.renderObject<RenderEditable>(
find.byElementPredicate((Element element) => element.renderObject is RenderEditable) find.byElementPredicate((Element element) => element.renderObject is RenderEditable)
).text.style.color, ).text.style.color,
CupertinoColors.white, isSameColorAs(CupertinoColors.white),
); );
}, },
); );
...@@ -2899,7 +2899,7 @@ void main() { ...@@ -2899,7 +2899,7 @@ void main() {
); );
await tester.pump(); await tester.pump();
expect(renderEditable.cursorColor, CupertinoColors.activeOrange.darkColor); expect(renderEditable.cursorColor, CupertinoColors.activeBlue.darkColor);
await tester.pumpWidget( await tester.pumpWidget(
const CupertinoApp( const CupertinoApp(
......
...@@ -59,7 +59,7 @@ void main() { ...@@ -59,7 +59,7 @@ void main() {
primaryColor: CupertinoColors.destructiveRed, primaryColor: CupertinoColors.destructiveRed,
)); ));
expect(theme.textTheme.actionTextStyle.color, CupertinoColors.destructiveRed); expect(theme.textTheme.actionTextStyle.color, isSameColorAs(CupertinoColors.destructiveRed));
}); });
testWidgets('Dependent attribute can be overridden from cascaded value', (WidgetTester tester) async { testWidgets('Dependent attribute can be overridden from cascaded value', (WidgetTester tester) async {
...@@ -71,9 +71,9 @@ void main() { ...@@ -71,9 +71,9 @@ void main() {
)); ));
// The brightness still cascaded down to the background color. // The brightness still cascaded down to the background color.
expect(theme.scaffoldBackgroundColor, CupertinoColors.black); expect(theme.scaffoldBackgroundColor, isSameColorAs(CupertinoColors.black));
// But not to the font color which we overrode. // But not to the font color which we overrode.
expect(theme.textTheme.textStyle.color, CupertinoColors.black); expect(theme.textTheme.textStyle.color, isSameColorAs(CupertinoColors.black));
}); });
testWidgets( testWidgets(
...@@ -125,24 +125,32 @@ void main() { ...@@ -125,24 +125,32 @@ void main() {
); );
final CupertinoThemeData theme = await testTheme(tester, originalTheme.copyWith( final CupertinoThemeData theme = await testTheme(tester, originalTheme.copyWith(
primaryColor: CupertinoColors.activeGreen, primaryColor: CupertinoColors.systemGreen,
)); ));
expect(theme.brightness, Brightness.dark); expect(theme.brightness, Brightness.dark);
expect(theme.primaryColor.value, CupertinoColors.systemGreen.darkColor.value); expect(theme.primaryColor, isSameColorAs(CupertinoColors.systemGreen.darkColor));
// Now check calculated derivatives. // Now check calculated derivatives.
expect(theme.textTheme.actionTextStyle.color.value, CupertinoColors.systemGreen.darkColor.value); expect(theme.textTheme.actionTextStyle.color, isSameColorAs(CupertinoColors.systemGreen.darkColor));
expect(theme.scaffoldBackgroundColor.value, CupertinoColors.black.value); expect(theme.scaffoldBackgroundColor, isSameColorAs(CupertinoColors.black));
}, },
); );
testWidgets("Theme has default IconThemeData, which is derived from the theme's primary color", (WidgetTester tester) async { testWidgets("Theme has default IconThemeData, which is derived from the theme's primary color", (WidgetTester tester) async {
const Color primaryColor = CupertinoColors.destructiveRed; const CupertinoDynamicColor primaryColor = CupertinoColors.destructiveRed;
const CupertinoThemeData themeData = CupertinoThemeData(primaryColor: primaryColor); const CupertinoThemeData themeData = CupertinoThemeData(primaryColor: primaryColor);
final IconThemeData resultingIconTheme = await testIconTheme(tester, themeData); final IconThemeData resultingIconTheme = await testIconTheme(tester, themeData);
expect(resultingIconTheme.color, themeData.primaryColor); expect(resultingIconTheme.color, themeData.primaryColor);
// Works in dark mode if primaryColor is a CupertinoDynamicColor.
final Color darkColor = (await testIconTheme(
tester,
themeData.copyWith(brightness: Brightness.dark),
)).color;
expect(darkColor, isSameColorAs(primaryColor.darkColor));
}); });
testWidgets('IconTheme.of creates a dependency on iconTheme', (WidgetTester tester) async { testWidgets('IconTheme.of creates a dependency on iconTheme', (WidgetTester tester) async {
...@@ -155,4 +163,56 @@ void main() { ...@@ -155,4 +163,56 @@ void main() {
expect(buildCount, 2); expect(buildCount, 2);
expect(iconTheme.color, CupertinoColors.activeOrange); expect(iconTheme.color, CupertinoColors.activeOrange);
}); });
Brightness currentBrightness;
void colorMatches(Color componentColor, CupertinoDynamicColor expectedDynamicColor) {
switch (currentBrightness) {
case Brightness.light:
expect(componentColor, isSameColorAs(expectedDynamicColor.color));
break;
case Brightness.dark:
expect(componentColor, isSameColorAs(expectedDynamicColor.darkColor));
break;
}
}
final Function dynamicColorsTestGroup = () {
testWidgets('CupertinoTheme.of resolves colors', (WidgetTester tester) async {
final CupertinoThemeData data = CupertinoThemeData(brightness: currentBrightness, primaryColor: CupertinoColors.systemRed);
final CupertinoThemeData theme = await testTheme(tester, data);
expect(data.primaryColor, isSameColorAs(CupertinoColors.systemRed.color));
colorMatches(theme.primaryColor, CupertinoColors.systemRed);
});
testWidgets('CupertinoTheme.of resolves default values', (WidgetTester tester) async {
const CupertinoDynamicColor primaryColor = CupertinoColors.systemRed;
final CupertinoThemeData data = CupertinoThemeData(brightness: currentBrightness, primaryColor: primaryColor);
const CupertinoDynamicColor barBackgroundColor = CupertinoDynamicColor.withBrightness(
color: Color(0xF0F9F9F9),
darkColor: Color(0xF01D1D1D),
);
final CupertinoThemeData theme = await testTheme(tester, data);
colorMatches(theme.primaryContrastingColor, CupertinoColors.systemBackground);
colorMatches(theme.barBackgroundColor, barBackgroundColor);
colorMatches(theme.scaffoldBackgroundColor, CupertinoColors.systemBackground);
colorMatches(theme.textTheme.textStyle.color, CupertinoColors.label);
colorMatches(theme.textTheme.actionTextStyle.color, primaryColor);
colorMatches(theme.textTheme.tabLabelTextStyle.color, CupertinoColors.inactiveGray);
colorMatches(theme.textTheme.navTitleTextStyle.color, CupertinoColors.label);
colorMatches(theme.textTheme.navLargeTitleTextStyle.color, CupertinoColors.label);
colorMatches(theme.textTheme.navActionTextStyle.color, primaryColor);
colorMatches(theme.textTheme.pickerTextStyle.color, CupertinoColors.label);
colorMatches(theme.textTheme.dateTimePickerTextStyle.color, CupertinoColors.label);
});
};
currentBrightness = Brightness.light;
group('light colors', dynamicColorsTestGroup);
currentBrightness = Brightness.dark;
group('dark colors', dynamicColorsTestGroup);
} }
...@@ -127,6 +127,12 @@ const Matcher isInCard = _IsInCard(); ...@@ -127,6 +127,12 @@ const Matcher isInCard = _IsInCard();
/// * [isInCard], the opposite. /// * [isInCard], the opposite.
const Matcher isNotInCard = _IsNotInCard(); const Matcher isNotInCard = _IsNotInCard();
/// Asserts that the object represents the same color as [color] when used to paint.
///
/// Specifically this matcher checks the object is of type [Color] and its [Color.value]
/// equals to that of the given [color].
Matcher isSameColorAs(Color color) => _ColorMatcher(targetColor: color);
/// Asserts that an object's toString() is a plausible one-line description. /// Asserts that an object's toString() is a plausible one-line description.
/// ///
/// Specifically, this matcher checks that the string does not contains newline /// Specifically, this matcher checks that the string does not contains newline
...@@ -1609,6 +1615,24 @@ class _CoversSameAreaAs extends Matcher { ...@@ -1609,6 +1615,24 @@ class _CoversSameAreaAs extends Matcher {
description.add('covers expected area and only expected area'); description.add('covers expected area and only expected area');
} }
class _ColorMatcher extends Matcher {
const _ColorMatcher({
@required this.targetColor,
}) : assert(targetColor != null);
final Color targetColor;
@override
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
if (item is Color)
return item.value == targetColor.value;
return false;
}
@override
Description describe(Description description) => description.add('matches color $targetColor');
}
Future<ui.Image> _captureImage(Element element) { Future<ui.Image> _captureImage(Element element) {
RenderObject renderObject = element.renderObject; RenderObject renderObject = element.renderObject;
while (!renderObject.isRepaintBoundary) { while (!renderObject.isRepaintBoundary) {
......
...@@ -237,6 +237,33 @@ void main() { ...@@ -237,6 +237,33 @@ void main() {
); );
}); });
test('isSameColorAs', () {
expect(
const Color(0x87654321),
isSameColorAs(_CustomColor(0x87654321)),
);
expect(
_CustomColor(0x87654321),
isSameColorAs(const Color(0x87654321)),
);
expect(
const Color(0x12345678),
isNot(isSameColorAs(_CustomColor(0x87654321))),
);
expect(
_CustomColor(0x87654321),
isNot(isSameColorAs(const Color(0x12345678))),
);
expect(
_CustomColor(0xFF123456),
isSameColorAs(_CustomColor(0xFF123456)..isEqual = false),
);
});
group('coversSameAreaAs', () { group('coversSameAreaAs', () {
test('empty Paths', () { test('empty Paths', () {
expect( expect(
...@@ -676,3 +703,14 @@ class _FakeSemanticsNode extends SemanticsNode { ...@@ -676,3 +703,14 @@ class _FakeSemanticsNode extends SemanticsNode {
@override @override
SemanticsData getSemanticsData() => data; SemanticsData getSemanticsData() => data;
} }
class _CustomColor extends Color {
_CustomColor(int value) : super(value);
bool isEqual;
@override
bool operator ==(dynamic other) => isEqual ?? super == other;
@override
int get hashCode => hashValues(super.hashCode, isEqual);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment