| // Copyright 2014 The Flutter 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/foundation.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter/widgets.dart'; |
| |
| import 'colors.dart'; |
| import 'icon_theme_data.dart'; |
| import 'text_theme.dart'; |
| |
| export 'package:flutter/services.dart' show Brightness; |
| |
| // Values derived from https://developer.apple.com/design/resources/. |
| const _CupertinoThemeDefaults _kDefaultTheme = _CupertinoThemeDefaults( |
| 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. |
| /// |
| /// Affects the color and text styles of Cupertino widgets whose styling |
| /// are not overridden when constructing the respective widgets instances. |
| /// |
| /// Descendant widgets can retrieve the current [CupertinoThemeData] by calling |
| /// [CupertinoTheme.of]. An [InheritedWidget] dependency is created when |
| /// an ancestor [CupertinoThemeData] is retrieved via [CupertinoTheme.of]. |
| /// |
| /// The [CupertinoTheme] widget implies an [IconTheme] widget, whose |
| /// [IconTheme.data] has the same color as [CupertinoThemeData.primaryColor] |
| /// |
| /// See also: |
| /// |
| /// * [CupertinoThemeData], specifies the theme's visual styling. |
| /// * [CupertinoApp], which will automatically add a [CupertinoTheme] based on the |
| /// value of [CupertinoApp.theme]. |
| /// * [Theme], a Material theme which will automatically add a [CupertinoTheme] |
| /// with a [CupertinoThemeData] derived from the Material [ThemeData]. |
| class CupertinoTheme extends StatelessWidget { |
| /// Creates a [CupertinoTheme] to change descendant Cupertino widgets' styling. |
| /// |
| /// The [data] and [child] parameters must not be null. |
| const CupertinoTheme({ |
| Key key, |
| @required this.data, |
| @required this.child, |
| }) : assert(child != null), |
| assert(data != null), |
| super(key: key); |
| |
| /// The [CupertinoThemeData] styling for this theme. |
| final CupertinoThemeData data; |
| |
| /// Retrieves the [CupertinoThemeData] from the closest ancestor [CupertinoTheme] |
| /// widget, or a default [CupertinoThemeData] if no [CupertinoTheme] ancestor |
| /// exists. |
| /// |
| /// Resolves all the colors defined in that [CupertinoThemeData] against the |
| /// given [BuildContext] on a best-effort basis. |
| static CupertinoThemeData of(BuildContext context) { |
| final _InheritedCupertinoTheme inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedCupertinoTheme>(); |
| return (inheritedTheme?.theme?.data ?? const CupertinoThemeData()).resolveFrom(context, nullOk: true); |
| } |
| |
| /// Retrieves the [Brightness] to use for descendant Cupertino widgets, based |
| /// on the value of [CupertinoThemeData.brightness] in the given [context]. |
| /// |
| /// If no [CupertinoTheme] can be found in the given [context], or its `brightness` |
| /// is null, it will fall back to [MediaQueryData.brightness]. |
| /// |
| /// Throws an exception if no valid [CupertinoTheme] or [MediaQuery] widgets |
| /// exist in the ancestry tree, unless [nullOk] is set to true. |
| /// |
| /// See also: |
| /// |
| /// * [CupertinoThemeData.brightness], the property takes precedence over |
| /// [MediaQueryData.platformBrightness] for descendant Cupertino widgets. |
| static Brightness brightnessOf(BuildContext context, { bool nullOk = false }) { |
| final _InheritedCupertinoTheme inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedCupertinoTheme>(); |
| return inheritedTheme?.theme?.data?.brightness ?? MediaQuery.of(context, nullOk: nullOk)?.platformBrightness; |
| } |
| |
| /// The widget below this widget in the tree. |
| /// |
| /// {@macro flutter.widgets.child} |
| final Widget child; |
| |
| @override |
| Widget build(BuildContext context) { |
| return _InheritedCupertinoTheme( |
| theme: this, |
| child: IconTheme( |
| data: CupertinoIconThemeData(color: data.primaryColor), |
| child: child, |
| ), |
| ); |
| } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| data.debugFillProperties(properties); |
| } |
| } |
| |
| class _InheritedCupertinoTheme extends InheritedWidget { |
| const _InheritedCupertinoTheme({ |
| Key key, |
| @required this.theme, |
| @required Widget child, |
| }) : assert(theme != null), |
| super(key: key, child: child); |
| |
| final CupertinoTheme theme; |
| |
| @override |
| bool updateShouldNotify(_InheritedCupertinoTheme old) => theme.data != old.theme.data; |
| } |
| |
| /// Styling specifications for a [CupertinoTheme]. |
| /// |
| /// All constructor parameters can be null, in which case a |
| /// [CupertinoColors.activeBlue] based default iOS theme styling is used. |
| /// |
| /// Parameters can also be partially specified, in which case some parameters |
| /// will cascade down to other dependent parameters to create a cohesive |
| /// visual effect. For instance, if a [primaryColor] is specified, it would |
| /// cascade down to affect some fonts in [textTheme] if [textTheme] is not |
| /// specified. |
| /// |
| /// See also: |
| /// |
| /// * [CupertinoTheme], in which this [CupertinoThemeData] is inserted. |
| /// * [ThemeData], a Material equivalent that also configures Cupertino |
| /// styling via a [CupertinoThemeData] subclass [MaterialBasedCupertinoThemeData]. |
| @immutable |
| class CupertinoThemeData with Diagnosticable { |
| /// Creates a [CupertinoTheme] styling specification. |
| /// |
| /// Unspecified parameters default to a reasonable iOS default style. |
| const CupertinoThemeData({ |
| Brightness brightness, |
| Color primaryColor, |
| Color primaryContrastingColor, |
| CupertinoTextThemeData textTheme, |
| Color barBackgroundColor, |
| Color scaffoldBackgroundColor, |
| }) : this.raw( |
| brightness, |
| primaryColor, |
| primaryContrastingColor, |
| textTheme, |
| barBackgroundColor, |
| scaffoldBackgroundColor, |
| ); |
| |
| /// Same as the default constructor but with positional arguments to avoid |
| /// forgetting any and to specify all arguments. |
| /// |
| /// Used by subclasses to get the superclass's defaulting behaviors. |
| @protected |
| 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._primaryColor, |
| this._primaryContrastingColor, |
| this._textTheme, |
| this._barBackgroundColor, |
| this._scaffoldBackgroundColor, |
| this._defaults, |
| ); |
| |
| final _CupertinoThemeDefaults _defaults; |
| |
| /// The brightness override for Cupertino descendants. |
| /// |
| /// Defaults to null. If a non-null [Brightness] is specified, the value will |
| /// take precedence over the ambient [MediaQueryData.platformBrightness], when |
| /// determining the brightness of descendant Cupertino widgets. |
| /// |
| /// If coming from a Material [Theme] and unspecified, [brightness] will be |
| /// derived from the Material [ThemeData]'s `brightness`. |
| /// |
| /// See also: |
| /// |
| /// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers |
| /// [brightness] to its Material [Theme] parent if it's unspecified. |
| /// |
| /// * [CupertinoTheme.brightnessOf], a method used to retrieve the overall |
| /// [Brightness] from a [BuildContext], for Cupertino widgets. |
| final Brightness brightness; |
| |
| /// A color used on interactive elements of the theme. |
| /// |
| /// This color is generally used on text and icons in buttons and tappable |
| /// elements. Defaults to [CupertinoColors.activeBlue]. |
| /// |
| /// If coming from a Material [Theme] and unspecified, [primaryColor] will be |
| /// derived from the Material [ThemeData]'s `colorScheme.primary`. However, in |
| /// iOS styling, the [primaryColor] is more sparsely used than in Material |
| /// Design where the [primaryColor] can appear on non-interactive surfaces like |
| /// the [AppBar] background, [TextField] borders etc. |
| /// |
| /// See also: |
| /// |
| /// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers |
| /// [primaryColor] to its Material [Theme] parent if it's unspecified. |
| Color get primaryColor => _primaryColor ?? _defaults.primaryColor; |
| final Color _primaryColor; |
| |
| /// 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 |
| /// when the button's background is [primaryColor]. |
| /// |
| /// If coming from a Material [Theme] and unspecified, [primaryContrastingColor] |
| /// will be derived from the Material [ThemeData]'s `colorScheme.onPrimary`. |
| /// |
| /// See also: |
| /// |
| /// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers |
| /// [primaryContrastingColor] to its Material [Theme] parent if it's unspecified. |
| Color get primaryContrastingColor => _primaryContrastingColor ?? _defaults.primaryContrastingColor; |
| final Color _primaryContrastingColor; |
| |
| /// Text styles used by Cupertino widgets. |
| /// |
| /// Derived from [primaryColor] if unspecified. |
| CupertinoTextThemeData get textTheme { |
| return _textTheme ?? _defaults.textThemeDefaults.createDefaults(primaryColor: primaryColor); |
| } |
| final CupertinoTextThemeData _textTheme; |
| |
| /// Background color of the top nav bar and bottom tab bar. |
| /// |
| /// Defaults to a light gray in light mode, or a dark translucent gray color in |
| /// dark mode. |
| Color get barBackgroundColor => _barBackgroundColor ?? _defaults.barBackgroundColor; |
| final Color _barBackgroundColor; |
| |
| /// Background color of the scaffold. |
| /// |
| /// Defaults to [CupertinoColors.systemBackground]. |
| Color get scaffoldBackgroundColor => _scaffoldBackgroundColor ?? _defaults.scaffoldBackgroundColor; |
| final Color _scaffoldBackgroundColor; |
| |
| /// Returns an instance of the [CupertinoThemeData] whose property getters |
| /// only return the construction time specifications with no derived values. |
| /// |
| /// Used in Material themes to let unspecified properties fallback to Material |
| /// theme properties instead of iOS defaults. |
| CupertinoThemeData noDefault() { |
| return _NoDefaultCupertinoThemeData( |
| brightness, |
| _primaryColor, |
| _primaryContrastingColor, |
| _textTheme, |
| _barBackgroundColor, |
| _scaffoldBackgroundColor, |
| ); |
| } |
| |
| /// Returns a new `CupertinoThemeData` with all its colors resolved against the |
| /// given [BuildContext]. |
| /// |
| /// Called by [CupertinoTheme.of] to resolve colors defined in the retrieved |
| /// [CupertinoThemeData]. |
| @protected |
| CupertinoThemeData resolveFrom(BuildContext context, { bool nullOk = false }) { |
| Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context, nullOk: nullOk); |
| |
| return CupertinoThemeData._rawWithDefaults( |
| brightness, |
| convertColor(_primaryColor), |
| convertColor(_primaryContrastingColor), |
| _textTheme?.resolveFrom(context, nullOk: nullOk), |
| convertColor(_barBackgroundColor), |
| convertColor(_scaffoldBackgroundColor), |
| _defaults.resolveFrom(context, _textTheme == null, nullOk: nullOk), |
| ); |
| } |
| |
| /// Creates a copy of [CupertinoThemeData] with specified attributes overridden. |
| /// |
| /// Only the current instance's specified attributes are copied instead of |
| /// derived values. For instance, if the current [CupertinoThemeData.textTheme] |
| /// is implied from the current [primaryColor] because it was not specified, |
| /// copying with a different [primaryColor] will also change the copy's implied |
| /// [textTheme]. |
| CupertinoThemeData copyWith({ |
| Brightness brightness, |
| Color primaryColor, |
| Color primaryContrastingColor, |
| CupertinoTextThemeData textTheme, |
| Color barBackgroundColor, |
| Color scaffoldBackgroundColor, |
| }) { |
| return CupertinoThemeData._rawWithDefaults( |
| brightness ?? this.brightness, |
| primaryColor ?? _primaryColor, |
| primaryContrastingColor ?? _primaryContrastingColor, |
| textTheme ?? _textTheme, |
| barBackgroundColor ?? _barBackgroundColor, |
| scaffoldBackgroundColor ?? _scaffoldBackgroundColor, |
| _defaults, |
| ); |
| } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| const CupertinoThemeData defaultData = CupertinoThemeData(); |
| properties.add(EnumProperty<Brightness>('brightness', brightness, defaultValue: null)); |
| properties.add(createCupertinoColorProperty('primaryColor', primaryColor, defaultValue: defaultData.primaryColor)); |
| properties.add(createCupertinoColorProperty('primaryContrastingColor', primaryContrastingColor, defaultValue: defaultData.primaryContrastingColor)); |
| properties.add(createCupertinoColorProperty('barBackgroundColor', barBackgroundColor, defaultValue: defaultData.barBackgroundColor)); |
| properties.add(createCupertinoColorProperty('scaffoldBackgroundColor', scaffoldBackgroundColor, defaultValue: defaultData.scaffoldBackgroundColor)); |
| textTheme.debugFillProperties(properties); |
| } |
| } |
| |
| class _NoDefaultCupertinoThemeData extends CupertinoThemeData { |
| const _NoDefaultCupertinoThemeData( |
| Brightness brightness, |
| this.primaryColor, |
| this.primaryContrastingColor, |
| this.textTheme, |
| this.barBackgroundColor, |
| this.scaffoldBackgroundColor, |
| ) : super._rawWithDefaults( |
| brightness, |
| primaryColor, |
| primaryContrastingColor, |
| textTheme, |
| barBackgroundColor, |
| scaffoldBackgroundColor, |
| null, |
| ); |
| |
| @override |
| final Color primaryColor; |
| @override |
| final Color primaryContrastingColor; |
| @override |
| final CupertinoTextThemeData textTheme; |
| @override |
| final Color barBackgroundColor; |
| @override |
| final Color scaffoldBackgroundColor; |
| |
| @override |
| _NoDefaultCupertinoThemeData resolveFrom(BuildContext context, { bool nullOk = false }) { |
| Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context, nullOk: nullOk); |
| |
| return _NoDefaultCupertinoThemeData( |
| brightness, |
| convertColor(primaryColor), |
| convertColor(primaryContrastingColor), |
| textTheme?.resolveFrom(context, nullOk: nullOk), |
| convertColor(barBackgroundColor), |
| convertColor(scaffoldBackgroundColor), |
| ); |
| } |
| |
| @override |
| CupertinoThemeData copyWith({ |
| Brightness brightness, |
| Color primaryColor, |
| Color primaryContrastingColor, |
| CupertinoTextThemeData textTheme, |
| Color barBackgroundColor , |
| Color scaffoldBackgroundColor, |
| }) { |
| return _NoDefaultCupertinoThemeData( |
| brightness ?? this.brightness, |
| primaryColor ?? this.primaryColor, |
| primaryContrastingColor ?? this.primaryContrastingColor, |
| textTheme ?? this.textTheme, |
| barBackgroundColor ?? this.barBackgroundColor, |
| scaffoldBackgroundColor ?? this.scaffoldBackgroundColor, |
| ); |
| } |
| } |
| |
| @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, bool resolveTextTheme, { @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), |
| resolveTextTheme ? textThemeDefaults?.resolveFrom(context, nullOk: nullOk) : textThemeDefaults, |
| ); |
| } |
| } |
| |
| @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); |
| } |