diff --git a/packages/flutter/lib/src/material/material_localizations.dart b/packages/flutter/lib/src/material/material_localizations.dart index a06e245c4a..9b429c2caa 100644 --- a/packages/flutter/lib/src/material/material_localizations.dart +++ b/packages/flutter/lib/src/material/material_localizations.dart @@ -101,12 +101,16 @@ abstract class MaterialLocalizations { /// /// This text theme is incomplete. For example, it lacks text color /// information. This theme must be merged with another text theme that - /// provides the missing values. The text styles provided by this theme have - /// their [TextStyle.inherit] property set to true. + /// provides the missing values. /// /// Typically a complete theme is obtained via [Theme.of], which can be /// localized using the [Localizations] widget. /// + /// The text styles provided by this theme are expected to have their + /// [TextStyle.inherit] property set to false, so that the [ThemeData] + /// obtained from [Theme.of] no longer inherits text style properties and + /// contains a complete set of properties needed to style a [Text] widget. + /// /// See also: https://material.io/guidelines/style/typography.html TextTheme get localTextGeometry; diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index 15110fc5f9..ca05dbbba2 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -462,11 +462,13 @@ class ThemeData { /// Caches localized themes to speed up the [localize] method. static final _FifoCache<_IdentityThemeDataCacheKey, ThemeData> _localizedThemeDataCache = new _FifoCache<_IdentityThemeDataCacheKey, ThemeData>(_localizedThemeDataCacheSize); - /// Returns a new theme built by merging [baseTheme] into the text geometry - /// provided by the [localTextGeometry]. + /// Returns a new theme built by merging the text geometry provided by the + /// [localTextGeometry] theme with the [baseTheme]. /// - /// The [TextStyle.inherit] field in the text styles provided by - /// [localTextGeometry] must be set to true. + /// For those text styles in the [baseTheme] whose [TextStyle.inherit] is set + /// to true, the returned theme's text styles inherit the geometric properties + /// of [localTextGeometry]. The resulting text styles' [TextStyle.inherit] is + /// set to those provided by [localTextGeometry]. static ThemeData localize(ThemeData baseTheme, TextTheme localTextGeometry) { // WARNING: this method memoizes the result in a cache based on the // previously seen baseTheme and localTextGeometry. Memoization is safe diff --git a/packages/flutter/lib/src/material/typography.dart b/packages/flutter/lib/src/material/typography.dart index 8e16c31572..0903cec943 100644 --- a/packages/flutter/lib/src/material/typography.dart +++ b/packages/flutter/lib/src/material/typography.dart @@ -134,6 +134,11 @@ class TextTheme { /// Creates a new [TextTheme] where each text style from this object has been /// merged with the matching text style from the `other` object. /// + /// The merging is done by calling [TextStyle.merge] on each respective pair + /// of text styles from this and the [other] text themes and is subject to + /// the value of [TextStyle.inherit] flag. For more details, see the + /// documentation on [TextStyle.merge] and [TextStyle.inherit]. + /// /// This is particularly useful if one [TextTheme] defines one set of /// properties and another defines a different set, e.g. having colors defined /// in one text theme and font sizes in another. @@ -170,71 +175,107 @@ class TextTheme { double fontSizeFactor: 1.0, double fontSizeDelta: 0.0, Color displayColor, - Color bodyColor + Color bodyColor, + TextDecoration decoration, + Color decorationColor, + TextDecorationStyle decorationStyle, }) { return new TextTheme( display4: display4.apply( color: displayColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), display3: display3.apply( color: displayColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), display2: display2.apply( color: displayColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), display1: display1.apply( color: displayColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), headline: headline.apply( color: bodyColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), title: title.apply( color: bodyColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), subhead: subhead.apply( color: bodyColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), body2: body2.apply( color: bodyColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), body1: body1.apply( color: bodyColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), caption: caption.apply( color: displayColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, ), button: button.apply( color: bodyColor, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, fontFamily: fontFamily, fontSizeFactor: fontSizeFactor, fontSizeDelta: fontSizeDelta, @@ -353,59 +394,59 @@ class Typography { // TODO(yjbanov): implement font fallback (see "Font stack" at https://material.io/guidelines/style/typography.html) class _MaterialTextColorThemes { static const TextTheme blackMountainView = const TextTheme( - display4: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54), - display3: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54), - display2: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54), - display1: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54), - headline: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87), - title : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87), - subhead : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87), - body2 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87), - body1 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87), - caption : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black54), - button : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.black87), + display4: const TextStyle(debugLabel: 'blackMountainView display4', fontFamily: 'Roboto', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + display3: const TextStyle(debugLabel: 'blackMountainView display3', fontFamily: 'Roboto', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + display2: const TextStyle(debugLabel: 'blackMountainView display2', fontFamily: 'Roboto', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + display1: const TextStyle(debugLabel: 'blackMountainView display1', fontFamily: 'Roboto', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + headline: const TextStyle(debugLabel: 'blackMountainView headline', fontFamily: 'Roboto', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + title : const TextStyle(debugLabel: 'blackMountainView title', fontFamily: 'Roboto', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + subhead : const TextStyle(debugLabel: 'blackMountainView subhead', fontFamily: 'Roboto', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + body2 : const TextStyle(debugLabel: 'blackMountainView body2', fontFamily: 'Roboto', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + body1 : const TextStyle(debugLabel: 'blackMountainView body1', fontFamily: 'Roboto', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + caption : const TextStyle(debugLabel: 'blackMountainView caption', fontFamily: 'Roboto', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + button : const TextStyle(debugLabel: 'blackMountainView button', fontFamily: 'Roboto', inherit: true, color: Colors.black87, decoration: TextDecoration.none), ); static const TextTheme whiteMountainView = const TextTheme( - display4: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70), - display3: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70), - display2: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70), - display1: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70), - headline: const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white), - title : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white), - subhead : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white), - body2 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white), - body1 : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white), - caption : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white70), - button : const TextStyle(fontFamily: 'Roboto', inherit: true, color: Colors.white), + display4: const TextStyle(debugLabel: 'whiteMountainView display4', fontFamily: 'Roboto', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + display3: const TextStyle(debugLabel: 'whiteMountainView display3', fontFamily: 'Roboto', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + display2: const TextStyle(debugLabel: 'whiteMountainView display2', fontFamily: 'Roboto', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + display1: const TextStyle(debugLabel: 'whiteMountainView display1', fontFamily: 'Roboto', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + headline: const TextStyle(debugLabel: 'whiteMountainView headline', fontFamily: 'Roboto', inherit: true, color: Colors.white, decoration: TextDecoration.none), + title : const TextStyle(debugLabel: 'whiteMountainView title', fontFamily: 'Roboto', inherit: true, color: Colors.white, decoration: TextDecoration.none), + subhead : const TextStyle(debugLabel: 'whiteMountainView subhead', fontFamily: 'Roboto', inherit: true, color: Colors.white, decoration: TextDecoration.none), + body2 : const TextStyle(debugLabel: 'whiteMountainView body2', fontFamily: 'Roboto', inherit: true, color: Colors.white, decoration: TextDecoration.none), + body1 : const TextStyle(debugLabel: 'whiteMountainView body1', fontFamily: 'Roboto', inherit: true, color: Colors.white, decoration: TextDecoration.none), + caption : const TextStyle(debugLabel: 'whiteMountainView caption', fontFamily: 'Roboto', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + button : const TextStyle(debugLabel: 'whiteMountainView button', fontFamily: 'Roboto', inherit: true, color: Colors.white, decoration: TextDecoration.none), ); static const TextTheme blackCupertino = const TextTheme( - display4: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54), - display3: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54), - display2: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54), - display1: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black54), - headline: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black87), - title : const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.black87), - subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87), - body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87), - body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87), - caption : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black54), - button : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.black87), + display4: const TextStyle(debugLabel: 'blackCupertino display4', fontFamily: '.SF UI Display', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + display3: const TextStyle(debugLabel: 'blackCupertino display3', fontFamily: '.SF UI Display', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + display2: const TextStyle(debugLabel: 'blackCupertino display2', fontFamily: '.SF UI Display', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + display1: const TextStyle(debugLabel: 'blackCupertino display1', fontFamily: '.SF UI Display', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + headline: const TextStyle(debugLabel: 'blackCupertino headline', fontFamily: '.SF UI Display', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + title : const TextStyle(debugLabel: 'blackCupertino title', fontFamily: '.SF UI Display', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + subhead : const TextStyle(debugLabel: 'blackCupertino subhead', fontFamily: '.SF UI Text', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + body2 : const TextStyle(debugLabel: 'blackCupertino body2', fontFamily: '.SF UI Text', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + body1 : const TextStyle(debugLabel: 'blackCupertino body1', fontFamily: '.SF UI Text', inherit: true, color: Colors.black87, decoration: TextDecoration.none), + caption : const TextStyle(debugLabel: 'blackCupertino caption', fontFamily: '.SF UI Text', inherit: true, color: Colors.black54, decoration: TextDecoration.none), + button : const TextStyle(debugLabel: 'blackCupertino button', fontFamily: '.SF UI Text', inherit: true, color: Colors.black87, decoration: TextDecoration.none), ); static const TextTheme whiteCupertino = const TextTheme( - display4: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70), - display3: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70), - display2: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70), - display1: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white70), - headline: const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white), - title : const TextStyle(fontFamily: '.SF UI Display', inherit: true, color: Colors.white), - subhead : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white), - body2 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white), - body1 : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white), - caption : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white70), - button : const TextStyle(fontFamily: '.SF UI Text', inherit: true, color: Colors.white), + display4: const TextStyle(debugLabel: 'whiteCupertino display4', fontFamily: '.SF UI Display', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + display3: const TextStyle(debugLabel: 'whiteCupertino display3', fontFamily: '.SF UI Display', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + display2: const TextStyle(debugLabel: 'whiteCupertino display2', fontFamily: '.SF UI Display', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + display1: const TextStyle(debugLabel: 'whiteCupertino display1', fontFamily: '.SF UI Display', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + headline: const TextStyle(debugLabel: 'whiteCupertino headline', fontFamily: '.SF UI Display', inherit: true, color: Colors.white, decoration: TextDecoration.none), + title : const TextStyle(debugLabel: 'whiteCupertino title', fontFamily: '.SF UI Display', inherit: true, color: Colors.white, decoration: TextDecoration.none), + subhead : const TextStyle(debugLabel: 'whiteCupertino subhead', fontFamily: '.SF UI Text', inherit: true, color: Colors.white, decoration: TextDecoration.none), + body2 : const TextStyle(debugLabel: 'whiteCupertino body2', fontFamily: '.SF UI Text', inherit: true, color: Colors.white, decoration: TextDecoration.none), + body1 : const TextStyle(debugLabel: 'whiteCupertino body1', fontFamily: '.SF UI Text', inherit: true, color: Colors.white, decoration: TextDecoration.none), + caption : const TextStyle(debugLabel: 'whiteCupertino caption', fontFamily: '.SF UI Text', inherit: true, color: Colors.white70, decoration: TextDecoration.none), + button : const TextStyle(debugLabel: 'whiteCupertino button', fontFamily: '.SF UI Text', inherit: true, color: Colors.white, decoration: TextDecoration.none), ); } @@ -444,46 +485,46 @@ class MaterialTextGeometry { /// Defines text geometry for English-like scripts, such as English, French, Russian, etc. static const TextTheme englishLike = const TextTheme( - display4: const TextStyle(fontSize: 112.0, fontWeight: FontWeight.w100, textBaseline: TextBaseline.alphabetic), - display3: const TextStyle(fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - display2: const TextStyle(fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - display1: const TextStyle(fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - headline: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - title : const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic), - subhead : const TextStyle(fontSize: 16.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - body2 : const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic), - body1 : const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - caption : const TextStyle(fontSize: 12.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - button : const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic), + display4: const TextStyle(debugLabel: 'englishLike display4', inherit: false, fontSize: 112.0, fontWeight: FontWeight.w100, textBaseline: TextBaseline.alphabetic), + display3: const TextStyle(debugLabel: 'englishLike display3', inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + display2: const TextStyle(debugLabel: 'englishLike display2', inherit: false, fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + display1: const TextStyle(debugLabel: 'englishLike display1', inherit: false, fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + headline: const TextStyle(debugLabel: 'englishLike headline', inherit: false, fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + title : const TextStyle(debugLabel: 'englishLike title', inherit: false, fontSize: 20.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic), + subhead : const TextStyle(debugLabel: 'englishLike subhead', inherit: false, fontSize: 16.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + body2 : const TextStyle(debugLabel: 'englishLike body2', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic), + body1 : const TextStyle(debugLabel: 'englishLike body1', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + caption : const TextStyle(debugLabel: 'englishLike caption', inherit: false, fontSize: 12.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + button : const TextStyle(debugLabel: 'englishLike button', inherit: false, fontSize: 14.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic), ); /// Defines text geometry for dense scripts, such as Chinese, Japanese, Korean, etc. static const TextTheme dense = const TextTheme( - display4: const TextStyle(fontSize: 112.0, fontWeight: FontWeight.w100, textBaseline: TextBaseline.ideographic), - display3: const TextStyle(fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), - display2: const TextStyle(fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), - display1: const TextStyle(fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), - headline: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), - title : const TextStyle(fontSize: 21.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), - subhead : const TextStyle(fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), - body2 : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), - body1 : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), - caption : const TextStyle(fontSize: 13.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), - button : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), + display4: const TextStyle(debugLabel: 'dense display4', inherit: false, fontSize: 112.0, fontWeight: FontWeight.w100, textBaseline: TextBaseline.ideographic), + display3: const TextStyle(debugLabel: 'dense display3', inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), + display2: const TextStyle(debugLabel: 'dense display2', inherit: false, fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), + display1: const TextStyle(debugLabel: 'dense display1', inherit: false, fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), + headline: const TextStyle(debugLabel: 'dense headline', inherit: false, fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), + title : const TextStyle(debugLabel: 'dense title', inherit: false, fontSize: 21.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), + subhead : const TextStyle(debugLabel: 'dense subhead', inherit: false, fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), + body2 : const TextStyle(debugLabel: 'dense body2', inherit: false, fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), + body1 : const TextStyle(debugLabel: 'dense body1', inherit: false, fontSize: 15.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), + caption : const TextStyle(debugLabel: 'dense caption', inherit: false, fontSize: 13.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), + button : const TextStyle(debugLabel: 'dense button', inherit: false, fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), ); /// Defines text geometry for tall scripts, such as Farsi, Hindi, Thai, etc. static const TextTheme tall = const TextTheme( - display4: const TextStyle(fontSize: 112.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - display3: const TextStyle(fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - display2: const TextStyle(fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - display1: const TextStyle(fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - headline: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - title : const TextStyle(fontSize: 21.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), - subhead : const TextStyle(fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - body2 : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), - body1 : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - caption : const TextStyle(fontSize: 13.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), - button : const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), + display4: const TextStyle(debugLabel: 'tall display4', inherit: false, fontSize: 112.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + display3: const TextStyle(debugLabel: 'tall display3', inherit: false, fontSize: 56.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + display2: const TextStyle(debugLabel: 'tall display2', inherit: false, fontSize: 45.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + display1: const TextStyle(debugLabel: 'tall display1', inherit: false, fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + headline: const TextStyle(debugLabel: 'tall headline', inherit: false, fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + title : const TextStyle(debugLabel: 'tall title', inherit: false, fontSize: 21.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), + subhead : const TextStyle(debugLabel: 'tall subhead', inherit: false, fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + body2 : const TextStyle(debugLabel: 'tall body2', inherit: false, fontSize: 15.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), + body1 : const TextStyle(debugLabel: 'tall body1', inherit: false, fontSize: 15.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + caption : const TextStyle(debugLabel: 'tall caption', inherit: false, fontSize: 13.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), + button : const TextStyle(debugLabel: 'tall button', inherit: false, fontSize: 15.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), ); } diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index 4068a7a5cf..98b10c6a9b 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -8,6 +8,8 @@ import 'package:flutter/foundation.dart'; import 'basic_types.dart'; +const String _kDefaultDebugLabel = 'unknown'; + /// An immutable style in which paint text. /// /// ## Sample code @@ -230,6 +232,7 @@ class TextStyle extends Diagnosticable { this.decoration, this.decorationColor, this.decorationStyle, + this.debugLabel, String fontFamily, String package, }) : fontFamily = package == null ? fontFamily : 'packages/$package/$fontFamily', @@ -302,6 +305,19 @@ class TextStyle extends Diagnosticable { /// The style in which to paint the text decorations (e.g., dashed). final TextDecorationStyle decorationStyle; + /// A human-readable description of this text style. + /// + /// This property is maintained only in debug builds. + /// + /// When merging ([merge]), copying ([copyWith]), modifying using [apply], or + /// interpolating ([lerp]), the label of the resulting style is marked with + /// the debug labels of the original styles. This helps figuring out where a + /// particular text style came from. + /// + /// This property is not considered when comparing text styles using `==` or + /// [compareTo], and it does not affect [hashCode]. + final String debugLabel; + /// Creates a copy of this text style but with the given fields replaced with /// the new values. TextStyle copyWith({ @@ -317,7 +333,14 @@ class TextStyle extends Diagnosticable { TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, + String debugLabel, }) { + String newDebugLabel; + assert(() { + if (this.debugLabel != null) + newDebugLabel = debugLabel ?? 'copy of ${this.debugLabel}'; + return true; + }()); return new TextStyle( inherit: inherit, color: color ?? this.color, @@ -332,11 +355,18 @@ class TextStyle extends Diagnosticable { decoration: decoration ?? this.decoration, decorationColor: decorationColor ?? this.decorationColor, decorationStyle: decorationStyle ?? this.decorationStyle, + debugLabel: newDebugLabel, ); } - /// Creates a copy of this text style but with the numeric fields multiplied - /// by the given factors and then incremented by the given deltas. + /// Creates a copy of this text style replacing or altering the specified + /// properties. + /// + /// The non-numeric properties [color], [fontFamily], [decoration], + /// [decorationColor] and [decorationStyle] are replaced with the new values. + /// + /// The numeric properties are multiplied by the given factors and then + /// incremented by the given deltas. /// /// For example, `style.apply(fontSizeFactor: 2.0, fontSizeDelta: 1.0)` would /// return a [TextStyle] whose [fontSize] is `style.fontSize * 2.0 + 1.0`. @@ -346,14 +376,15 @@ class TextStyle extends Diagnosticable { /// applied to a `style` whose [fontWeight] is [FontWeight.w500] will return a /// [TextStyle] with a [FontWeight.w300]. /// - /// The arguments must not be null. + /// The numeric arguments must not be null. /// /// If the underlying values are null, then the corresponding factors and/or /// deltas must not be specified. - /// - /// The non-numeric fields can be controlled using the corresponding arguments. TextStyle apply({ Color color, + TextDecoration decoration, + Color decorationColor, + TextDecorationStyle decorationStyle, String fontFamily, double fontSizeFactor: 1.0, double fontSizeDelta: 0.0, @@ -379,6 +410,14 @@ class TextStyle extends Diagnosticable { assert(heightFactor != null); assert(heightDelta != null); assert(heightFactor != null || (heightFactor == 1.0 && heightDelta == 0.0)); + + String modifiedDebugLabel; + assert(() { + if (debugLabel != null) + modifiedDebugLabel = 'modified $debugLabel'; + return true; + }()); + return new TextStyle( inherit: inherit, color: color ?? this.color, @@ -390,19 +429,40 @@ class TextStyle extends Diagnosticable { wordSpacing: wordSpacing == null ? null : wordSpacing * wordSpacingFactor + wordSpacingDelta, textBaseline: textBaseline, height: height == null ? null : height * heightFactor + heightDelta, - decoration: decoration, - decorationColor: decorationColor, - decorationStyle: decorationStyle, + decoration: decoration ?? this.decoration, + decorationColor: decorationColor ?? this.decorationColor, + decorationStyle: decorationStyle ?? this.decorationStyle, + debugLabel: modifiedDebugLabel, ); } - /// Returns a new text style that matches this text style but with some values - /// replaced by the non-null parameters of the given text style. If the given - /// text style is null, simply returns this text style. + /// Returns a new text style that is a combination of this style and the given + /// [other] style. + /// + /// If the given [other] text style has its [TextStyle.inherit] set to true, + /// its null properties are replaced with the non-null properties of this text + /// style. The [other] style _inherits_ the properties of this style. Another + /// way to think of it is that the "missing" properties of the [other] style + /// are _filled_ by the properties of this style. + /// + /// If the given [other] text style has its [TextStyle.inherit] set to false, + /// returns the given [other] style unchanged. The [other] style does not + /// inherit properties of this style. + /// + /// If the given text style is null, returns this text style. TextStyle merge(TextStyle other) { if (other == null) return this; - assert(other.inherit); + if (!other.inherit) + return other; + + String mergedDebugLabel; + assert(() { + if (other.debugLabel != null || debugLabel != null) + mergedDebugLabel = '${other.debugLabel ?? _kDefaultDebugLabel} < ${debugLabel ?? _kDefaultDebugLabel}'; + return true; + }()); + return copyWith( color: other.color, fontFamily: other.fontFamily, @@ -415,7 +475,8 @@ class TextStyle extends Diagnosticable { height: other.height, decoration: other.decoration, decorationColor: other.decorationColor, - decorationStyle: other.decorationStyle + decorationStyle: other.decorationStyle, + debugLabel: mergedDebugLabel, ); } @@ -424,6 +485,13 @@ class TextStyle extends Diagnosticable { /// This will not work well if the styles don't set the same fields. static TextStyle lerp(TextStyle begin, TextStyle end, double t) { assert(begin.inherit == end.inherit); + + String lerpDebugLabel; + assert(() { + lerpDebugLabel = 'lerp(${begin.debugLabel ?? _kDefaultDebugLabel}, ${end.debugLabel ?? _kDefaultDebugLabel})'; + return true; + }()); + return new TextStyle( inherit: end.inherit, color: Color.lerp(begin.color, end.color, t), @@ -437,7 +505,8 @@ class TextStyle extends Diagnosticable { height: ui.lerpDouble(begin.height ?? end.height, end.height ?? begin.height, t), decoration: t < 0.5 ? begin.decoration : end.decoration, decorationColor: Color.lerp(begin.decorationColor, end.decorationColor, t), - decorationStyle: t < 0.5 ? begin.decorationStyle : end.decorationStyle + decorationStyle: t < 0.5 ? begin.decorationStyle : end.decorationStyle, + debugLabel: lerpDebugLabel, ); } @@ -564,6 +633,8 @@ class TextStyle extends Diagnosticable { @override void debugFillProperties(DiagnosticPropertiesBuilder properties, { String prefix: '' }) { super.debugFillProperties(properties); + if (debugLabel != null) + properties.add(new MessageProperty('${prefix}debugLabel', debugLabel)); final List styles = []; styles.add(new DiagnosticsProperty('${prefix}color', color, defaultValue: null)); styles.add(new StringProperty('${prefix}family', fontFamily, defaultValue: null, quoted: false)); diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index abed7b36ba..36decc9d8d 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -795,13 +795,7 @@ void main() { }); testWidgets('TextField with default helperStyle', (WidgetTester tester) async { - final ThemeData themeData = ThemeData.localize( - new ThemeData( - hintColor: Colors.blue[500], - ), - MaterialTextGeometry.forScriptCategory(MaterialTextGeometry.englishLikeCategory), - ); - + final ThemeData themeData = new ThemeData(hintColor: Colors.blue[500]); await tester.pumpWidget( overlay( child: new Theme( @@ -816,7 +810,7 @@ void main() { ); final Text helperText = tester.widget(find.text('helper text')); expect(helperText.style.color, themeData.hintColor); - expect(helperText.style.fontSize, themeData.textTheme.caption.fontSize); + expect(helperText.style.fontSize, MaterialTextGeometry.englishLike.caption.fontSize); }); testWidgets('TextField with specified helperStyle', (WidgetTester tester) async { diff --git a/packages/flutter/test/material/theme_data_test.dart b/packages/flutter/test/material/theme_data_test.dart index 33b4f307c3..c45581771b 100644 --- a/packages/flutter/test/material/theme_data_test.dart +++ b/packages/flutter/test/material/theme_data_test.dart @@ -24,7 +24,8 @@ void main() { for (TargetPlatform platform in TargetPlatform.values) { final ThemeData theme = new ThemeData(platform: platform); final Typography typography = new Typography(platform: platform); - expect(theme.textTheme, typography.black, reason: 'Not using default typography for $platform'); + expect(theme.textTheme, typography.black.apply(decoration: TextDecoration.none), + reason: 'Not using default typography for $platform'); } }); diff --git a/packages/flutter/test/material/theme_test.dart b/packages/flutter/test/material/theme_test.dart index a993da227a..926f9017dd 100644 --- a/packages/flutter/test/material/theme_test.dart +++ b/packages/flutter/test/material/theme_test.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/src/foundation/diagnostics.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -367,6 +369,56 @@ void main() { expect(actualFontSize, _kMagicFontSize); }); + + testWidgets('Default Theme provides all basic TextStyle properties', (WidgetTester tester) async { + ThemeData theme; + await tester.pumpWidget(new Directionality( + textDirection: TextDirection.ltr, + child: new Builder( + builder: (BuildContext context) { + theme = Theme.of(context); + return const Text('A'); + }, + ), + )); + + List extractStyles(TextTheme textTheme) { + return [ + textTheme.display4, + textTheme.display3, + textTheme.display2, + textTheme.display1, + textTheme.headline, + textTheme.title, + textTheme.subhead, + textTheme.body2, + textTheme.body1, + textTheme.caption, + textTheme.button, + ]; + } + + for (TextTheme textTheme in [theme.textTheme, theme.primaryTextTheme, theme.accentTextTheme]) { + for (TextStyle style in extractStyles(textTheme).map((TextStyle style) => new _TextStyleProxy(style))) { + expect(style.inherit, false); + expect(style.color, isNotNull); + expect(style.fontFamily, isNotNull); + expect(style.fontSize, isNotNull); + expect(style.fontWeight, isNotNull); + expect(style.fontStyle, null); + expect(style.letterSpacing, null); + expect(style.wordSpacing, null); + expect(style.textBaseline, isNotNull); + expect(style.height, null); + expect(style.decoration, TextDecoration.none); + expect(style.decorationColor, null); + expect(style.decorationStyle, null); + expect(style.debugLabel, isNotNull); + } + } + + expect(theme.textTheme.display4.debugLabel, 'blackMountainView display4 < englishLike display4'); + }); } int testBuildCalled; @@ -388,3 +440,73 @@ class _TestState extends State { ); } } + +/// This class exists only to make sure that we test all the properties of the +/// [TextStyle] class. If a property is added/removed/renamed, the analyzer will +/// complain that this class has incorrect overrides. +class _TextStyleProxy implements TextStyle { + _TextStyleProxy(this._delegate); + + final TextStyle _delegate; + + // Do make sure that all the properties correctly forward to the _delegate. + @override Color get color => _delegate.color; + @override String get debugLabel => _delegate.debugLabel; + @override TextDecoration get decoration => _delegate.decoration; + @override Color get decorationColor => _delegate.decorationColor; + @override TextDecorationStyle get decorationStyle => _delegate.decorationStyle; + @override String get fontFamily => _delegate.fontFamily; + @override double get fontSize => _delegate.fontSize; + @override FontStyle get fontStyle => _delegate.fontStyle; + @override FontWeight get fontWeight => _delegate.fontWeight; + @override double get height => _delegate.height; + @override bool get inherit => _delegate.inherit; + @override double get letterSpacing => _delegate.letterSpacing; + @override TextBaseline get textBaseline => _delegate.textBaseline; + @override double get wordSpacing => _delegate.wordSpacing; + + @override + DiagnosticsNode toDiagnosticsNode({String name, DiagnosticsTreeStyle style}) { + throw new UnimplementedError(); + } + + @override + String toStringShort() { + throw new UnimplementedError(); + } + + @override + TextStyle apply({Color color, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String fontFamily, double fontSizeFactor: 1.0, double fontSizeDelta: 0.0, int fontWeightDelta: 0, double letterSpacingFactor: 1.0, double letterSpacingDelta: 0.0, double wordSpacingFactor: 1.0, double wordSpacingDelta: 0.0, double heightFactor: 1.0, double heightDelta: 0.0}) { + throw new UnimplementedError(); + } + + @override + RenderComparison compareTo(TextStyle other) { + throw new UnimplementedError(); + } + + @override + TextStyle copyWith({Color color, String fontFamily, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double letterSpacing, double wordSpacing, TextBaseline textBaseline, double height, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String debugLabel}) { + throw new UnimplementedError(); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties, {String prefix: ''}) { + throw new UnimplementedError(); + } + + @override + ui.ParagraphStyle getParagraphStyle({TextAlign textAlign, TextDirection textDirection, double textScaleFactor: 1.0, String ellipsis, int maxLines}) { + throw new UnimplementedError(); + } + + @override + ui.TextStyle getTextStyle({double textScaleFactor: 1.0}) { + throw new UnimplementedError(); + } + + @override + TextStyle merge(TextStyle other) { + throw new UnimplementedError(); + } +} diff --git a/packages/flutter/test/painting/text_style_test.dart b/packages/flutter/test/painting/text_style_test.dart index 61e1aa01c4..fd2f607ce7 100644 --- a/packages/flutter/test/painting/text_style_test.dart +++ b/packages/flutter/test/painting/text_style_test.dart @@ -142,4 +142,25 @@ void main() { expect(s7.fontFamily, 'packages/p/test'); expect(s7.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: packages/p/test, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified)'); }); + + test('TextStyle.debugLabel', () { + const TextStyle unknown = const TextStyle(); + const TextStyle foo = const TextStyle(debugLabel: 'foo', fontSize: 1.0); + const TextStyle bar = const TextStyle(debugLabel: 'bar', fontSize: 2.0); + const TextStyle baz = const TextStyle(debugLabel: 'baz', fontSize: 3.0); + + expect(unknown.debugLabel, null); + expect(unknown.toString(), 'TextStyle()'); + expect(unknown.copyWith().debugLabel, null); + expect(unknown.apply().debugLabel, null); + + expect(foo.debugLabel, 'foo'); + expect(foo.toString(), 'TextStyle(debugLabel: foo, inherit: true, size: 1.0)'); + expect(foo.merge(bar).debugLabel, 'bar < foo'); + expect(foo.merge(bar).merge(baz).debugLabel, 'baz < bar < foo'); + expect(foo.copyWith().debugLabel, 'copy of foo'); + expect(foo.apply().debugLabel, 'modified foo'); + expect(TextStyle.lerp(foo, bar, 0.5).debugLabel, 'lerp(foo, bar)'); + expect(TextStyle.lerp(foo.merge(bar), baz, 0.5).copyWith().debugLabel, 'copy of lerp(bar < foo, baz)'); + }); }