diff --git a/flutter_frontend_server/test/to_string_test.dart b/flutter_frontend_server/test/to_string_test.dart index ca1f14c90adf9..770f5866ac669 100644 --- a/flutter_frontend_server/test/to_string_test.dart +++ b/flutter_frontend_server/test/to_string_test.dart @@ -60,7 +60,8 @@ void main() { ])); final runResult = io.Process.runSync(dart, [regularDill]); checkProcessResult(runResult); - var paintString = '"Paint.toString":"Paint(Color(0xffffffff))"'; + var paintString = + '"Paint.toString":"Paint(Color(alpha: 1.0000, red: 1.0000, green: 1.0000, blue: 1.0000, colorSpace: ColorSpace.sRGB))"'; if (buildDir.contains('release')) { paintString = '"Paint.toString":"Instance of \'Paint\'"'; } diff --git a/lib/ui/lerp.dart b/lib/ui/lerp.dart index 5b17fe86756e3..b90e8a7738c51 100644 --- a/lib/ui/lerp.dart +++ b/lib/ui/lerp.dart @@ -37,15 +37,3 @@ double _lerpDouble(double a, double b, double t) { double _lerpInt(int a, int b, double t) { return a + (b - a) * t; } - -/// Same as [num.clamp] but specialized for non-null [int]. -int _clampInt(int value, int min, int max) { - assert(min <= max); - if (value < min) { - return min; - } else if (value > max) { - return max; - } else { - return value; - } -} diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index f15737cd07198..09ef1c5e8a4c3 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -48,8 +48,8 @@ bool _radiusIsValid(Radius radius) { return true; } -Color _scaleAlpha(Color a, double factor) { - return a.withAlpha((a.alpha * factor).round().clamp(0, 255)); +Color _scaleAlpha(Color x, double factor) { + return x.withValues(alpha: clampDouble(x.a * factor, 0, 1)); } /// An immutable 32 bit color value in ARGB format. @@ -310,10 +310,11 @@ class Color { /// /// See . double computeLuminance() { + assert(colorSpace != ColorSpace.extendedSRGB); // See - final double R = _linearizeColorComponent(red / 0xFF); - final double G = _linearizeColorComponent(green / 0xFF); - final double B = _linearizeColorComponent(blue / 0xFF); + final double R = _linearizeColorComponent(r); + final double G = _linearizeColorComponent(g); + final double B = _linearizeColorComponent(b); return 0.2126 * R + 0.7152 * G + 0.0722 * B; } @@ -339,28 +340,26 @@ class Color { /// /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. - static Color? lerp(Color? a, Color? b, double t) { - // TODO(gaaclarke): Update math to use floats. This was already attempted - // but it leads to subtle changes that change test results. - assert(a?.colorSpace != ColorSpace.extendedSRGB); - assert(b?.colorSpace != ColorSpace.extendedSRGB); - if (b == null) { - if (a == null) { + static Color? lerp(Color? x, Color? y, double t) { + assert(x?.colorSpace != ColorSpace.extendedSRGB); + assert(y?.colorSpace != ColorSpace.extendedSRGB); + if (y == null) { + if (x == null) { return null; } else { - return _scaleAlpha(a, 1.0 - t); + return _scaleAlpha(x, 1.0 - t); } } else { - if (a == null) { - return _scaleAlpha(b, t); + if (x == null) { + return _scaleAlpha(y, t); } else { - assert(a.colorSpace == b.colorSpace); - return Color._fromARGBC( - _clampInt(_lerpInt(a.alpha, b.alpha, t).toInt(), 0, 255), - _clampInt(_lerpInt(a.red, b.red, t).toInt(), 0, 255), - _clampInt(_lerpInt(a.green, b.green, t).toInt(), 0, 255), - _clampInt(_lerpInt(a.blue, b.blue, t).toInt(), 0, 255), - a.colorSpace, + assert(x.colorSpace == y.colorSpace); + return Color.from( + alpha: clampDouble(_lerpDouble(x.a, y.a, t), 0, 1), + red: clampDouble(_lerpDouble(x.r, y.r, t), 0, 1), + green: clampDouble(_lerpDouble(x.g, y.g, t), 0, 1), + blue: clampDouble(_lerpDouble(x.b, y.b, t), 0, 1), + colorSpace: x.colorSpace, ); } } @@ -377,32 +376,30 @@ class Color { static Color alphaBlend(Color foreground, Color background) { assert(foreground.colorSpace == background.colorSpace); assert(foreground.colorSpace != ColorSpace.extendedSRGB); - // TODO(gaaclarke): Update math to use floats. This was already attempted - // but it leads to subtle changes that change test results. - final int alpha = foreground.alpha; - if (alpha == 0x00) { // Foreground completely transparent. + final double alpha = foreground.a; + if (alpha == 0) { // Foreground completely transparent. return background; } - final int invAlpha = 0xff - alpha; - int backAlpha = background.alpha; - if (backAlpha == 0xff) { // Opaque background case - return Color._fromARGBC( - 0xff, - (alpha * foreground.red + invAlpha * background.red) ~/ 0xff, - (alpha * foreground.green + invAlpha * background.green) ~/ 0xff, - (alpha * foreground.blue + invAlpha * background.blue) ~/ 0xff, - foreground.colorSpace, + final double invAlpha = 1 - alpha; + double backAlpha = background.a; + if (backAlpha == 1) { // Opaque background case + return Color.from( + alpha: 1, + red: alpha * foreground.r + invAlpha * background.r, + green: alpha * foreground.g + invAlpha * background.g, + blue: alpha * foreground.b + invAlpha * background.b, + colorSpace: foreground.colorSpace, ); } else { // General case - backAlpha = (backAlpha * invAlpha) ~/ 0xff; - final int outAlpha = alpha + backAlpha; - assert(outAlpha != 0x00); - return Color._fromARGBC( - outAlpha, - (foreground.red * alpha + background.red * backAlpha) ~/ outAlpha, - (foreground.green * alpha + background.green * backAlpha) ~/ outAlpha, - (foreground.blue * alpha + background.blue * backAlpha) ~/ outAlpha, - foreground.colorSpace, + backAlpha = backAlpha * invAlpha; + final double outAlpha = alpha + backAlpha; + assert(outAlpha != 0); + return Color.from( + alpha: outAlpha, + red: (foreground.r * alpha + background.r * backAlpha) / outAlpha, + green: (foreground.g * alpha + background.g * backAlpha) / outAlpha, + blue: (foreground.b * alpha + background.b * backAlpha) / outAlpha, + colorSpace: foreground.colorSpace, ); } } @@ -423,16 +420,19 @@ class Color { return false; } return other is Color && - other.value == value && + other.a == a && + other.r == r && + other.g == g && + other.b == b && other.colorSpace == colorSpace; } @override - int get hashCode => Object.hash(value, colorSpace); + int get hashCode => Object.hash(a, r, g, b, colorSpace); - // TODO(gaaclarke): Make toString() print out float values. @override - String toString() => 'Color(0x${value.toRadixString(16).padLeft(8, '0')})'; + String toString() => + 'Color(alpha: ${a.toStringAsFixed(4)}, red: ${r.toStringAsFixed(4)}, green: ${g.toStringAsFixed(4)}, blue: ${b.toStringAsFixed(4)}, colorSpace: $colorSpace)'; } /// Algorithms to use when painting on the canvas. diff --git a/lib/web_ui/lib/lerp.dart b/lib/web_ui/lib/lerp.dart index ff70c354e308f..80a941f7890e8 100644 --- a/lib/web_ui/lib/lerp.dart +++ b/lib/web_ui/lib/lerp.dart @@ -28,10 +28,3 @@ double? lerpDouble(num? a, num? b, double t) { double _lerpDouble(double a, double b, double t) { return a * (1.0 - t) + b * t; } - -/// Linearly interpolate between two integers. -/// -/// Same as [lerpDouble] but specialized for non-null `int` type. -double _lerpInt(int a, int b, double t) { - return a * (1.0 - t) + b * t; -} diff --git a/lib/web_ui/lib/painting.dart b/lib/web_ui/lib/painting.dart index 113cabadca1e9..7bdd746633328 100644 --- a/lib/web_ui/lib/painting.dart +++ b/lib/web_ui/lib/painting.dart @@ -17,8 +17,8 @@ void _validateColorStops(List colors, List? colorStops) { } } -Color _scaleAlpha(Color a, double factor) { - return a.withAlpha(engine.clampInt((a.alpha * factor).round(), 0, 255)); +Color _scaleAlpha(Color x, double factor) { + return x.withValues(alpha: (x.a * factor).clamp(0, 1)); } class Color { @@ -141,32 +141,32 @@ class Color { double computeLuminance() { // See - final double R = _linearizeColorComponent(red / 0xFF); - final double G = _linearizeColorComponent(green / 0xFF); - final double B = _linearizeColorComponent(blue / 0xFF); + final double R = _linearizeColorComponent(r); + final double G = _linearizeColorComponent(g); + final double B = _linearizeColorComponent(b); return 0.2126 * R + 0.7152 * G + 0.0722 * B; } - static Color? lerp(Color? a, Color? b, double t) { - assert(a?.colorSpace != ColorSpace.extendedSRGB); - assert(b?.colorSpace != ColorSpace.extendedSRGB); - if (b == null) { - if (a == null) { + static Color? lerp(Color? x, Color? y, double t) { + assert(x?.colorSpace != ColorSpace.extendedSRGB); + assert(y?.colorSpace != ColorSpace.extendedSRGB); + if (y == null) { + if (x == null) { return null; } else { - return _scaleAlpha(a, 1.0 - t); + return _scaleAlpha(x, 1.0 - t); } } else { - if (a == null) { - return _scaleAlpha(b, t); + if (x == null) { + return _scaleAlpha(y, t); } else { - assert(a.colorSpace == b.colorSpace); - return Color._fromARGBC( - engine.clampInt(_lerpInt(a.alpha, b.alpha, t).toInt(), 0, 255), - engine.clampInt(_lerpInt(a.red, b.red, t).toInt(), 0, 255), - engine.clampInt(_lerpInt(a.green, b.green, t).toInt(), 0, 255), - engine.clampInt(_lerpInt(a.blue, b.blue, t).toInt(), 0, 255), - a.colorSpace, + assert(x.colorSpace == y.colorSpace); + return Color.from( + alpha: _lerpDouble(x.a, y.a, t).clamp(0, 1), + red: _lerpDouble(x.r, y.r, t).clamp(0, 1), + green: _lerpDouble(x.g, y.g, t).clamp(0, 1), + blue: _lerpDouble(x.b, y.b, t).clamp(0, 1), + colorSpace: x.colorSpace, ); } } @@ -175,30 +175,30 @@ class Color { static Color alphaBlend(Color foreground, Color background) { assert(foreground.colorSpace == background.colorSpace); assert(foreground.colorSpace != ColorSpace.extendedSRGB); - final int alpha = foreground.alpha; - if (alpha == 0x00) { + final double alpha = foreground.a; + if (alpha == 0) { // Foreground completely transparent. return background; } - final int invAlpha = 0xff - alpha; - int backAlpha = background.alpha; - if (backAlpha == 0xff) { - return Color._fromARGBC( - 0xff, - (alpha * foreground.red + invAlpha * background.red) ~/ 0xff, - (alpha * foreground.green + invAlpha * background.green) ~/ 0xff, - (alpha * foreground.blue + invAlpha * background.blue) ~/ 0xff, - foreground.colorSpace, + final double invAlpha = 1 - alpha; + double backAlpha = background.a; + if (backAlpha == 1) { // Opaque background case + return Color.from( + alpha: 1, + red: alpha * foreground.r + invAlpha * background.r, + green: alpha * foreground.g + invAlpha * background.g, + blue: alpha * foreground.b + invAlpha * background.b, + colorSpace: foreground.colorSpace, ); - } else { - backAlpha = (backAlpha * invAlpha) ~/ 0xff; - final int outAlpha = alpha + backAlpha; - assert(outAlpha != 0x00); - return Color._fromARGBC( - outAlpha, - (foreground.red * alpha + background.red * backAlpha) ~/ outAlpha, - (foreground.green * alpha + background.green * backAlpha) ~/ outAlpha, - (foreground.blue * alpha + background.blue * backAlpha) ~/ outAlpha, - foreground.colorSpace, + } else { // General case + backAlpha = backAlpha * invAlpha; + final double outAlpha = alpha + backAlpha; + assert(outAlpha != 0); + return Color.from( + alpha: outAlpha, + red: (foreground.r * alpha + background.r * backAlpha) / outAlpha, + green: (foreground.g * alpha + background.g * backAlpha) / outAlpha, + blue: (foreground.b * alpha + background.b * backAlpha) / outAlpha, + colorSpace: foreground.colorSpace, ); } } @@ -216,15 +216,19 @@ class Color { return false; } return other is Color && - other.value == value && + other.a == a && + other.r == r && + other.g == g && + other.b == b && other.colorSpace == colorSpace; } @override - int get hashCode => Object.hash(value, colorSpace); + int get hashCode => Object.hash(a, r, g, b, colorSpace); @override - String toString() => 'Color(0x${value.toRadixString(16).padLeft(8, '0')})'; + String toString() => + 'Color(alpha: ${a.toStringAsFixed(4)}, red: ${r.toStringAsFixed(4)}, green: ${g.toStringAsFixed(4)}, blue: ${b.toStringAsFixed(4)}, colorSpace: $colorSpace)'; } enum StrokeCap { diff --git a/lib/web_ui/test/ui/color_test.dart b/lib/web_ui/test/ui/color_test.dart index 2ac2df7595d02..6ee06e7f8a734 100644 --- a/lib/web_ui/test/ui/color_test.dart +++ b/lib/web_ui/test/ui/color_test.dart @@ -8,6 +8,33 @@ import 'package:ui/ui.dart'; import '../common/test_initialization.dart'; + +class _ColorMatcher extends Matcher { + _ColorMatcher(this._target, this._threshold); + + final Color _target; + final double _threshold; + + @override + Description describe(Description description) { + return description.add('matches color "$_target" with threshold "$_threshold".'); + } + + @override + bool matches(dynamic item, Map matchState) { + return item is Color && + item.colorSpace == _target.colorSpace && + (item.a - _target.a).abs() <= _threshold && + (item.r - _target.r).abs() <= _threshold && + (item.g - _target.g).abs() <= _threshold && + (item.b - _target.b).abs() <= _threshold; + } +} + +Matcher isSameColorAs(Color color, {double threshold = 0.004}) { + return _ColorMatcher(color, threshold); +} + void main() { internalBootstrapBrowserTest(() => testMain); } @@ -31,7 +58,7 @@ Future testMain() async { const Color c = Color(0x00000000); final Paint p = Paint(); p.color = c; - expect(c.toString(), equals('Color(0x00000000)')); + expect(c.toString(), equals('${const Color(0x00000000)}')); }); test('color created with out of bounds value', () { @@ -63,7 +90,7 @@ Future testMain() async { ); expect( Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), 0.5), - const Color(0x7F7F7F7F), + isSameColorAs(const Color(0x7F7F7F7F)), ); expect( Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), 1.0), @@ -102,23 +129,23 @@ Future testMain() async { ); expect( Color.alphaBlend(const Color(0x80808080), const Color(0xFFFFFFFF)), - const Color(0xFFBFBFBF), + isSameColorAs(const Color(0xFFBFBFBF)), ); expect( Color.alphaBlend(const Color(0x80808080), const Color(0xFF000000)), - const Color(0xFF404040), + isSameColorAs(const Color(0xFF404040)), ); expect( Color.alphaBlend(const Color(0x01020304), const Color(0xFF000000)), - const Color(0xFF000000), + isSameColorAs(const Color(0xFF000000)), ); expect( Color.alphaBlend(const Color(0x11223344), const Color(0xFF000000)), - const Color(0xFF020304), + isSameColorAs(const Color(0xFF020304)), ); expect( Color.alphaBlend(const Color(0x11223344), const Color(0x80000000)), - const Color(0x88040608), + isSameColorAs(const Color(0x88040608)), ); }); diff --git a/lib/web_ui/test/ui/filters_test.dart b/lib/web_ui/test/ui/filters_test.dart index d2ca97291548e..fc7ce4aa604aa 100644 --- a/lib/web_ui/test/ui/filters_test.dart +++ b/lib/web_ui/test/ui/filters_test.dart @@ -121,22 +121,22 @@ Future testMain() async { test('color filter as image filter', () async { const ui.ColorFilter colorFilter = ui.ColorFilter.mode( - ui.Color.fromRGBO(0, 0, 255, 128), + ui.Color.fromARGB(128, 0, 0, 255), ui.BlendMode.srcOver, ); await drawTestImageWithPaint(ui.Paint()..imageFilter = colorFilter); await matchGoldenFile('ui_filter_colorfilter_as_imagefilter.png', region: region); - expect(colorFilter.toString(), 'ColorFilter.mode(Color(0x800000ff), BlendMode.srcOver)'); + expect(colorFilter.toString(), 'ColorFilter.mode(${const ui.Color(0x800000ff)}, BlendMode.srcOver)'); }); test('mode color filter', () async { const ui.ColorFilter colorFilter = ui.ColorFilter.mode( - ui.Color.fromRGBO(0, 0, 255, 128), + ui.Color.fromARGB(128, 0, 0, 255), ui.BlendMode.srcOver, ); await drawTestImageWithPaint(ui.Paint()..colorFilter = colorFilter); await matchGoldenFile('ui_filter_mode_colorfilter.png', region: region); - expect(colorFilter.toString(), 'ColorFilter.mode(Color(0x800000ff), BlendMode.srcOver)'); + expect(colorFilter.toString(), 'ColorFilter.mode(${const ui.Color(0x800000ff)}, BlendMode.srcOver)'); }); test('linearToSRGBGamma color filter', () async { diff --git a/lib/web_ui/test/ui/paint_test.dart b/lib/web_ui/test/ui/paint_test.dart index 759aa636321d7..253ba3d17d38e 100644 --- a/lib/web_ui/test/ui/paint_test.dart +++ b/lib/web_ui/test/ui/paint_test.dart @@ -69,7 +69,7 @@ Future testMain() async { expect( paint.toString(), 'Paint(' - 'Color(0xaabbccdd); ' + '${const ui.Color(0xaabbccdd)}; ' 'BlendMode.darken; ' 'colorFilter: ColorFilter.linearToSrgbGamma(); ' 'maskFilter: MaskFilter.blur(BlurStyle.normal, 1.7); ' diff --git a/lib/web_ui/test/ui/text_style_test.dart b/lib/web_ui/test/ui/text_style_test.dart index 67eda1dedf007..98b5241946703 100644 --- a/lib/web_ui/test/ui/text_style_test.dart +++ b/lib/web_ui/test/ui/text_style_test.dart @@ -122,9 +122,9 @@ Future testMain() async { expect( style.toString(), 'TextStyle(' - 'color: Color(0xff000000), ' + 'color: ${const ui.Color(0xff000000)}, ' 'decoration: TextDecoration.none, ' - 'decorationColor: Color(0xffaa0000), ' + 'decorationColor: ${const ui.Color(0xffaa0000)}, ' 'decorationStyle: TextDecorationStyle.solid, ' 'decorationThickness: ${1.0}, ' 'fontWeight: FontWeight.w400, ' @@ -140,7 +140,7 @@ Future testMain() async { 'locale: en_US, ' 'background: Paint(), ' 'foreground: unspecified, ' - 'shadows: [TextShadow(Color(0xff000000), Offset(0.0, 0.0), ${0.0})], ' + 'shadows: [TextShadow(${const ui.Color(0xff000000)}, Offset(0.0, 0.0), ${0.0})], ' "fontFeatures: [FontFeature('case', 1)], " "fontVariations: [FontVariation('ital', 0.1)]" ')', @@ -165,7 +165,7 @@ Future testMain() async { 'TextStyle(' 'color: unspecified, ' 'decoration: TextDecoration.none, ' - 'decorationColor: Color(0xffaa0000), ' + 'decorationColor: ${const ui.Color(0xffaa0000)}, ' 'decorationStyle: TextDecorationStyle.solid, ' 'decorationThickness: ${1.0}, ' 'fontWeight: FontWeight.w400, ' @@ -181,7 +181,7 @@ Future testMain() async { 'locale: en_US, ' 'background: Paint(), ' 'foreground: Paint(), ' - 'shadows: [TextShadow(Color(0xff000000), Offset(0.0, 0.0), ${0.0})], ' + 'shadows: [TextShadow(${const ui.Color(0xff000000)}, Offset(0.0, 0.0), ${0.0})], ' "fontFeatures: [FontFeature('case', 1)], " "fontVariations: [FontVariation('ital', 0.1)]" ')', diff --git a/testing/dart/color_test.dart b/testing/dart/color_test.dart index 763339ae00b90..2b45535369e42 100644 --- a/testing/dart/color_test.dart +++ b/testing/dart/color_test.dart @@ -6,6 +6,32 @@ import 'dart:ui'; import 'package:test/test.dart'; +class _ColorMatcher extends Matcher { + _ColorMatcher(this._target, this._threshold); + + final Color _target; + final double _threshold; + + @override + Description describe(Description description) { + return description.add('matches color "$_target" with threshold "$_threshold".'); + } + + @override + bool matches(dynamic item, Map matchState) { + return item is Color && + item.colorSpace == _target.colorSpace && + (item.a - _target.a).abs() <= _threshold && + (item.r - _target.r).abs() <= _threshold && + (item.g - _target.g).abs() <= _threshold && + (item.b - _target.b).abs() <= _threshold; + } +} + +Matcher colorMatches(Color color, {double threshold = 1/255}) { + return _ColorMatcher(color, threshold); +} + class NotAColor extends Color { const NotAColor(super.value); } @@ -23,7 +49,7 @@ void main() { const Color c = Color(0x00000000); final Paint p = Paint(); p.color = c; - expect(c.toString(), equals('Color(0x00000000)')); + expect(c, equals(const Color(0x00000000))); }); test('color created with out of bounds value', () { @@ -54,7 +80,7 @@ void main() { ); expect( Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), 0.5), - const Color(0x7F7F7F7F), + colorMatches(const Color(0x7F7F7F7F)), ); expect( Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), 1.0), @@ -144,23 +170,23 @@ void main() { ); expect( Color.alphaBlend(const Color(0x80808080), const Color(0xFFFFFFFF)), - const Color(0xFFBFBFBF), + colorMatches(const Color(0xFFBFBFBF)), ); expect( Color.alphaBlend(const Color(0x80808080), const Color(0xFF000000)), - const Color(0xFF404040), + colorMatches(const Color(0xFF404040)), ); expect( Color.alphaBlend(const Color(0x01020304), const Color(0xFF000000)), - const Color(0xFF000000), + colorMatches(const Color(0xFF000000)), ); expect( Color.alphaBlend(const Color(0x11223344), const Color(0xFF000000)), - const Color(0xFF020304), + colorMatches(const Color(0xFF020304)), ); expect( Color.alphaBlend(const Color(0x11223344), const Color(0x80000000)), - const Color(0x88040608), + colorMatches(const Color(0x88040608)), ); }); diff --git a/testing/dart/image_filter_test.dart b/testing/dart/image_filter_test.dart index 4ad33c0aa70f1..dab241627158d 100644 --- a/testing/dart/image_filter_test.dart +++ b/testing/dart/image_filter_test.dart @@ -293,7 +293,7 @@ void main() async { ).toString(), contains( 'matrix([10.0, 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.0, -0.0, 0.0, 1.0], FilterQuality.low) -> ' - 'ColorFilter.mode(Color(0xffabcdef), BlendMode.color) -> ' + 'ColorFilter.mode(${const Color(0xFFABCDEF)}, BlendMode.color) -> ' 'blur(20.0, 20.0, repeated) -> ' 'blur(30.0, 30.0, mirror)' ), diff --git a/testing/dart/text_test.dart b/testing/dart/text_test.dart index 108aefb7a53ed..46f176bca0d67 100644 --- a/testing/dart/text_test.dart +++ b/testing/dart/text_test.dart @@ -80,7 +80,7 @@ void testTextStyle() { ); expect( ts1.toString(), - equals('TextStyle(color: Color(0xff00ff00), decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)'), + equals('TextStyle(color: ${const Color(0xFF00FF00)}, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)'), ); expect( ts2.toString(),