Update Color to do all calculations with floating point components (#54981)

This transforms the rest of Color to use the floating point parameters.  This will likely break existing tests very subtly.  For example, colors will be slightly different in golden tests if `lerp` was ever used.

issue: https://github.com/flutter/flutter/issues/127855

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
diff --git a/flutter_frontend_server/test/to_string_test.dart b/flutter_frontend_server/test/to_string_test.dart
index 92a0a2a..fc21f4c 100644
--- a/flutter_frontend_server/test/to_string_test.dart
+++ b/flutter_frontend_server/test/to_string_test.dart
@@ -60,7 +60,8 @@
     ]));
     final runResult = io.Process.runSync(dart, <String>[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 5b17fe8..b90e8a7 100644
--- a/lib/ui/lerp.dart
+++ b/lib/ui/lerp.dart
@@ -37,15 +37,3 @@
 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 202f499..6a171e7 100644
--- a/lib/ui/painting.dart
+++ b/lib/ui/painting.dart
@@ -48,8 +48,8 @@
   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 @@
   ///
   /// See <https://en.wikipedia.org/wiki/Relative_luminance>.
   double computeLuminance() {
+    assert(colorSpace != ColorSpace.extendedSRGB);
     // See <https://www.w3.org/TR/WCAG20/#relativeluminancedef>
-    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 @@
   ///
   /// Values for `t` are usually obtained from an [Animation<double>], 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 @@
   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 @@
       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 ff70c35..80a941f 100644
--- a/lib/web_ui/lib/lerp.dart
+++ b/lib/web_ui/lib/lerp.dart
@@ -28,10 +28,3 @@
 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 113caba..7bdd746 100644
--- a/lib/web_ui/lib/painting.dart
+++ b/lib/web_ui/lib/painting.dart
@@ -17,8 +17,8 @@
   }
 }
 
-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 @@
 
   double computeLuminance() {
     // See <https://www.w3.org/TR/WCAG20/#relativeluminancedef>
-    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 @@
   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 @@
       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 2ac2df7..6ee06e7 100644
--- a/lib/web_ui/test/ui/color_test.dart
+++ b/lib/web_ui/test/ui/color_test.dart
@@ -8,6 +8,33 @@
 
 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<dynamic, dynamic> 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 @@
     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 @@
     );
     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 @@
     );
     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 d2ca972..fc7ce4a 100644
--- a/lib/web_ui/test/ui/filters_test.dart
+++ b/lib/web_ui/test/ui/filters_test.dart
@@ -121,22 +121,22 @@
 
   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 759aa63..253ba3d 100644
--- a/lib/web_ui/test/ui/paint_test.dart
+++ b/lib/web_ui/test/ui/paint_test.dart
@@ -69,7 +69,7 @@
     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 67eda1d..98b5241 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 @@
     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 @@
       '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 @@
       '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 @@
       '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 56e8766..f686bf4 100644
--- a/testing/dart/color_test.dart
+++ b/testing/dart/color_test.dart
@@ -6,6 +6,19 @@
 
 import 'package:litetest/litetest.dart';
 
+/// Positive result when the Colors will map to the same argb8888 color.
+Matcher colorMatches(dynamic o) => (v) {
+  Expect.isTrue(o is Color);
+  Expect.isTrue(v is Color);
+  if (o is Color && v is Color) {
+    Expect.equals(o.colorSpace, v.colorSpace);
+    Expect.approxEquals(o.a, v.a, 1 / 255);
+    Expect.approxEquals(o.r, v.r, 1 / 255);
+    Expect.approxEquals(o.g, v.g, 1 / 255);
+    Expect.approxEquals(o.b, v.b, 1 / 255);
+  }
+};
+
 class NotAColor extends Color {
   const NotAColor(super.value);
 }
@@ -27,7 +40,7 @@
     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', () {
@@ -58,7 +71,7 @@
     );
     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),
@@ -148,23 +161,23 @@
     );
     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 0200d6f..d017ff2 100644
--- a/testing/dart/image_filter_test.dart
+++ b/testing/dart/image_filter_test.dart
@@ -293,7 +293,7 @@
       ).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 108aefb..46f176b 100644
--- a/testing/dart/text_test.dart
+++ b/testing/dart/text_test.dart
@@ -80,7 +80,7 @@
     );
     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(),