diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index 04e06d09c7..3cfc71d3af 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -12,7 +12,10 @@ import 'strut_style.dart'; const String _kDefaultDebugLabel = 'unknown'; const String _kColorForegroundWarning = 'Cannot provide both a color and a foreground\n' - 'The color argument is just a shorthand for "foreground: new Paint()..color = color".'; + 'The color argument is just a shorthand for "foreground: new Paint()..color = color".'; + +const String _kColorBackgroundWarning = 'Cannot provide both a backgroundColor and a background\n' + 'The backgroundColor argument is just a shorthand for "background: new Paint()..color = color".'; // Examples can assume: // BuildContext context; @@ -60,6 +63,10 @@ const String _kColorForegroundWarning = 'Cannot provide both a color and a foreg /// If [color] is specified, [foreground] must be null and vice versa. [color] is /// treated as a shorthand for `Paint()..color = color`. /// +/// If [backgroundColor] is specified, [background] must be null and vice versa. +/// The [backgroundColor] is treated as a shorthand for +/// `background: Paint()..color = backgroundColor`. +/// /// ```dart /// RichText( /// text: TextSpan( @@ -295,6 +302,7 @@ class TextStyle extends Diagnosticable { const TextStyle({ this.inherit = true, this.color, + this.backgroundColor, this.fontSize, this.fontWeight, this.fontStyle, @@ -317,7 +325,8 @@ class TextStyle extends Diagnosticable { _fontFamilyFallback = fontFamilyFallback, _package = package, assert(inherit != null), - assert(color == null || foreground == null, _kColorForegroundWarning); + assert(color == null || foreground == null, _kColorForegroundWarning), + assert(backgroundColor == null || background == null, _kColorBackgroundWarning); /// Whether null values are replaced with their value in an ancestor text @@ -338,6 +347,17 @@ class TextStyle extends Diagnosticable { /// specified in one place, it will dominate [color] in another. final Color color; + /// The color to use as the background for the text. + /// + /// If [background] is specified, this value must be null. The + /// [backgroundColor] property is shorthand for + /// `background: Paint()..color = backgroundColor`. + /// + /// In [merge], [apply], and [lerp], conflicts between [backgroundColor] and [background] + /// specification are resolved in [background]'s favor - i.e. if [background] is + /// specified in one place, it will dominate [color] in another. + final Color backgroundColor; + /// The name of the font to use when painting the text (e.g., Roboto). If the /// font is defined in a package, this will be prefixed with /// 'packages/package_name/' (e.g. 'packages/cool_fonts/Roboto'). The @@ -450,6 +470,15 @@ class TextStyle extends Diagnosticable { /// styles are created with the same paint settings. Otherwise, each time it /// will appear like the style changed, which will result in unnecessary /// updates all the way through the framework. + /// + /// If [backgroundColor] is specified, this value must be null. The + /// [backgroundColor] property is shorthand for + /// `background: Paint()..color = backgroundColor`. + /// + /// In [merge], [apply], and [lerp], conflicts between [backgroundColor] and + /// [background] specification are resolved in [background]'s favor - i.e. if + /// [background] is specified in one place, it will dominate [backgroundColor] + /// in another. final Paint background; /// The decorations to paint near the text (e.g., an underline). @@ -488,8 +517,13 @@ class TextStyle extends Diagnosticable { /// /// One of [color] or [foreground] must be null, and if this has [foreground] /// specified it will be given preference over any color parameter. + /// + /// One of [backgroundColor] or [background] must be null, and if this has + /// [background] specified it will be given preference over any + /// backgroundColor parameter. TextStyle copyWith({ Color color, + Color backgroundColor, String fontFamily, List fontFamilyFallback, double fontSize, @@ -509,6 +543,7 @@ class TextStyle extends Diagnosticable { String debugLabel, }) { assert(color == null || foreground == null, _kColorForegroundWarning); + assert(backgroundColor == null || background == null, _kColorBackgroundWarning); String newDebugLabel; assert(() { if (this.debugLabel != null) @@ -518,6 +553,7 @@ class TextStyle extends Diagnosticable { return TextStyle( inherit: inherit, color: this.foreground == null && foreground == null ? color ?? this.color : null, + backgroundColor: this.background == null && background == null ? backgroundColor ?? this.backgroundColor : null, fontFamily: fontFamily ?? this.fontFamily, fontFamilyFallback: fontFamilyFallback ?? this.fontFamilyFallback, fontSize: fontSize ?? this.fontSize, @@ -544,7 +580,9 @@ class TextStyle extends Diagnosticable { /// The non-numeric properties [color], [fontFamily], [decoration], /// [decorationColor] and [decorationStyle] are replaced with the new values. /// - /// [foreground] will be given preference over [color] if it is not null. + /// [foreground] will be given preference over [color] if it is not null and + /// [background] will be given preference over [backgroundColor] if it is not + /// null. /// /// The numeric properties are multiplied by the given factors and then /// incremented by the given deltas. @@ -563,9 +601,11 @@ class TextStyle extends Diagnosticable { /// deltas must not be specified. /// /// If [foreground] is specified on this object, then applying [color] here - /// will have no effect. + /// will have no effect and if [background] is specified on this object, then + /// applying [backgroundColor] here will have no effect either. TextStyle apply({ Color color, + Color backgroundColor, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, @@ -606,6 +646,7 @@ class TextStyle extends Diagnosticable { return TextStyle( inherit: inherit, color: foreground == null ? color ?? this.color : null, + backgroundColor: background == null ? backgroundColor ?? this.backgroundColor : null, fontFamily: fontFamily ?? this.fontFamily, fontFamilyFallback: fontFamilyFallback ?? this.fontFamilyFallback, fontSize: fontSize == null ? null : fontSize * fontSizeFactor + fontSizeDelta, @@ -616,7 +657,7 @@ class TextStyle extends Diagnosticable { textBaseline: textBaseline, height: height == null ? null : height * heightFactor + heightDelta, locale: locale, - foreground: foreground != null ? foreground : null, + foreground: foreground, background: background, shadows: shadows, decoration: decoration ?? this.decoration, @@ -643,6 +684,10 @@ class TextStyle extends Diagnosticable { /// /// One of [color] or [foreground] must be null, and if this or `other` has /// [foreground] specified it will be given preference over any color parameter. + /// + /// Similarly, One of [backgroundColor] or [background] must be null, and if + /// this or `other` has [background] specified it will be given preference + /// over any backgroundColor parameter. TextStyle merge(TextStyle other) { if (other == null) return this; @@ -658,6 +703,7 @@ class TextStyle extends Diagnosticable { return copyWith( color: other.color, + backgroundColor: other.backgroundColor, fontFamily: other.fontFamily, fontFamilyFallback: other.fontFamilyFallback, fontSize: other.fontSize, @@ -687,6 +733,10 @@ class TextStyle extends Diagnosticable { /// If [foreground] is specified on either of `a` or `b`, both will be treated /// as if they have a [foreground] paint (creating a new [Paint] if necessary /// based on the [color] property). + /// + /// If [background] is specified on either of `a` or `b`, both will be treated + /// as if they have a [background] paint (creating a new [Paint] if necessary + /// based on the [backgroundColor] property). static TextStyle lerp(TextStyle a, TextStyle b, double t) { assert(t != null); assert(a == null || b == null || a.inherit == b.inherit); @@ -704,6 +754,7 @@ class TextStyle extends Diagnosticable { return TextStyle( inherit: b.inherit, color: Color.lerp(null, b.color, t), + backgroundColor: Color.lerp(null, b.backgroundColor, t), fontFamily: t < 0.5 ? null : b.fontFamily, fontFamilyFallback: t < 0.5 ? null : b.fontFamilyFallback, fontSize: t < 0.5 ? null : b.fontSize, @@ -728,6 +779,7 @@ class TextStyle extends Diagnosticable { return TextStyle( inherit: a.inherit, color: Color.lerp(a.color, null, t), + backgroundColor: Color.lerp(null, a.backgroundColor, t), fontFamily: t < 0.5 ? a.fontFamily : null, fontFamilyFallback: t < 0.5 ? a.fontFamilyFallback : null, fontSize: t < 0.5 ? a.fontSize : null, @@ -751,6 +803,7 @@ class TextStyle extends Diagnosticable { return TextStyle( inherit: b.inherit, color: a.foreground == null && b.foreground == null ? Color.lerp(a.color, b.color, t) : null, + backgroundColor: a.background == null && b.background == null ? Color.lerp(a.backgroundColor, b.backgroundColor, t) : null, fontFamily: t < 0.5 ? a.fontFamily : b.fontFamily, fontFamilyFallback: t < 0.5 ? a.fontFamilyFallback : b.fontFamilyFallback, fontSize: ui.lerpDouble(a.fontSize ?? b.fontSize, b.fontSize ?? a.fontSize, t), @@ -766,7 +819,11 @@ class TextStyle extends Diagnosticable { ? a.foreground ?? (Paint()..color = a.color) : b.foreground ?? (Paint()..color = b.color) : null, - background: t < 0.5 ? a.background : b.background, + background: (a.background != null || b.background != null) + ? t < 0.5 + ? a.background ?? (Paint()..color = a.backgroundColor) + : b.background ?? (Paint()..color = b.backgroundColor) + : null, shadows: t < 0.5 ? a.shadows : b.shadows, decoration: t < 0.5 ? a.decoration : b.decoration, decorationColor: Color.lerp(a.decorationColor, b.decorationColor, t), @@ -793,7 +850,10 @@ class TextStyle extends Diagnosticable { height: height, locale: locale, foreground: foreground, - background: background, + background: background ?? (backgroundColor != null + ? (Paint()..color = backgroundColor) + : null + ), shadows: shadows, ); } @@ -873,6 +933,7 @@ class TextStyle extends Diagnosticable { !listEquals(fontFamilyFallback, other.fontFamilyFallback)) return RenderComparison.layout; if (color != other.color || + backgroundColor != other.backgroundColor || decoration != other.decoration || decorationColor != other.decorationColor || decorationStyle != other.decorationStyle) @@ -889,6 +950,7 @@ class TextStyle extends Diagnosticable { final TextStyle typedOther = other; return inherit == typedOther.inherit && color == typedOther.color && + backgroundColor == typedOther.backgroundColor && fontFamily == typedOther.fontFamily && fontSize == typedOther.fontSize && fontWeight == typedOther.fontWeight && @@ -912,6 +974,7 @@ class TextStyle extends Diagnosticable { return hashValues( inherit, color, + backgroundColor, fontFamily, fontFamilyFallback, fontSize, @@ -942,6 +1005,7 @@ class TextStyle extends Diagnosticable { properties.add(MessageProperty('${prefix}debugLabel', debugLabel)); final List styles = []; styles.add(DiagnosticsProperty('${prefix}color', color, defaultValue: null)); + styles.add(DiagnosticsProperty('${prefix}backgroundColor', backgroundColor, defaultValue: null)); styles.add(StringProperty('${prefix}family', fontFamily, defaultValue: null, quoted: false)); styles.add(IterableProperty('${prefix}familyFallback', fontFamilyFallback, defaultValue: null)); styles.add(DoubleProperty('${prefix}size', fontSize, defaultValue: null)); diff --git a/packages/flutter/test/material/theme_test.dart b/packages/flutter/test/material/theme_test.dart index 739edc24e8..518a8da130 100644 --- a/packages/flutter/test/material/theme_test.dart +++ b/packages/flutter/test/material/theme_test.dart @@ -666,6 +666,7 @@ class _TextStyleProxy implements TextStyle { // Do make sure that all the properties correctly forward to the _delegate. @override Color get color => _delegate.color; + @override Color get backgroundColor => _delegate.backgroundColor; @override String get debugLabel => _delegate.debugLabel; @override TextDecoration get decoration => _delegate.decoration; @override Color get decorationColor => _delegate.decorationColor; @@ -700,7 +701,7 @@ class _TextStyleProxy implements TextStyle { } @override - TextStyle apply({ Color color, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String fontFamily, List fontFamilyFallback, 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 }) { + TextStyle apply({ Color color, Color backgroundColor, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String fontFamily, List fontFamilyFallback, 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 UnimplementedError(); } @@ -710,7 +711,7 @@ class _TextStyleProxy implements TextStyle { } @override - TextStyle copyWith({ Color color, String fontFamily, List fontFamilyFallback, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double letterSpacing, double wordSpacing, TextBaseline textBaseline, double height, Locale locale, ui.Paint foreground, ui.Paint background, List shadows, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String debugLabel }) { + TextStyle copyWith({ Color color, Color backgroundColor, String fontFamily, List fontFamilyFallback, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double letterSpacing, double wordSpacing, TextBaseline textBaseline, double height, Locale locale, ui.Paint foreground, ui.Paint background, List shadows, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String debugLabel }) { throw UnimplementedError(); } diff --git a/packages/flutter/test/painting/text_style_test.dart b/packages/flutter/test/painting/text_style_test.dart index f26570aec3..2372a394ea 100644 --- a/packages/flutter/test/painting/text_style_test.dart +++ b/packages/flutter/test/painting/text_style_test.dart @@ -294,4 +294,58 @@ void main() { expect(TextStyle.lerp(redPaintTextStyle, bluePaintTextStyle, .25).foreground.color, red); expect(TextStyle.lerp(redPaintTextStyle, bluePaintTextStyle, .75).foreground.color, blue); }); + + test('backgroundColor', () { + const TextStyle s1 = TextStyle(); + expect(s1.backgroundColor, isNull); + expect(s1.toString(), 'TextStyle()'); + + const TextStyle s2 = TextStyle(backgroundColor: Color(0xFF00FF00)); + expect(s2.backgroundColor, const Color(0xFF00FF00)); + expect(s2.toString(), 'TextStyle(inherit: true, backgroundColor: Color(0xff00ff00))'); + + final ui.TextStyle ts2 = s2.getTextStyle(); + expect(ts2.toString(), contains('background: Paint(Color(0xff00ff00))')); + }); + + test('TextStyle background and backgroundColor combos', () { + const Color red = Color.fromARGB(255, 255, 0, 0); + const Color blue = Color.fromARGB(255, 0, 0, 255); + const TextStyle redTextStyle = TextStyle(backgroundColor: red); + const TextStyle blueTextStyle = TextStyle(backgroundColor: blue); + final TextStyle redPaintTextStyle = TextStyle(background: Paint()..color = red); + final TextStyle bluePaintTextStyle = TextStyle(background: Paint()..color = blue); + + // merge/copyWith + final TextStyle redBlueBothForegroundMerged = redTextStyle.merge(blueTextStyle); + expect(redBlueBothForegroundMerged.backgroundColor, blue); + expect(redBlueBothForegroundMerged.foreground, isNull); + + final TextStyle redBlueBothPaintMerged = redPaintTextStyle.merge(bluePaintTextStyle); + expect(redBlueBothPaintMerged.backgroundColor, null); + expect(redBlueBothPaintMerged.background, bluePaintTextStyle.background); + + final TextStyle redPaintBlueColorMerged = redPaintTextStyle.merge(blueTextStyle); + expect(redPaintBlueColorMerged.backgroundColor, null); + expect(redPaintBlueColorMerged.background, redPaintTextStyle.background); + + final TextStyle blueColorRedPaintMerged = blueTextStyle.merge(redPaintTextStyle); + expect(blueColorRedPaintMerged.backgroundColor, null); + expect(blueColorRedPaintMerged.background, redPaintTextStyle.background); + + // apply + expect(redPaintTextStyle.apply(backgroundColor: blue).backgroundColor, isNull); + expect(redPaintTextStyle.apply(backgroundColor: blue).background.color, red); + expect(redTextStyle.apply(backgroundColor: blue).backgroundColor, blue); + + // lerp + expect(TextStyle.lerp(redTextStyle, blueTextStyle, .25).backgroundColor, Color.lerp(red, blue, .25)); + expect(TextStyle.lerp(redTextStyle, bluePaintTextStyle, .25).backgroundColor, isNull); + expect(TextStyle.lerp(redTextStyle, bluePaintTextStyle, .25).background.color, red); + expect(TextStyle.lerp(redTextStyle, bluePaintTextStyle, .75).background.color, blue); + + expect(TextStyle.lerp(redPaintTextStyle, bluePaintTextStyle, .25).backgroundColor, isNull); + expect(TextStyle.lerp(redPaintTextStyle, bluePaintTextStyle, .25).background.color, red); + expect(TextStyle.lerp(redPaintTextStyle, bluePaintTextStyle, .75).background.color, blue); + }); }