feat: Add paint layers to HasPaint and associated component renders (#2073)

This commit is contained in:
Matt Bennett
2022-10-24 23:37:24 +01:00
committed by GitHub
parent 03e1f33d3d
commit 9e6bf4fbcc
7 changed files with 253 additions and 109 deletions

View File

@ -34,7 +34,8 @@ class Ball extends CircleComponent
static const degree = math.pi / 180; static const degree = math.pi / 180;
@override @override
Future<void>? onLoad() { Future<void> onLoad() async {
super.onLoad();
_resetBall; _resetBall;
final hitBox = CircleHitbox( final hitBox = CircleHitbox(
radius: radius, radius: radius,
@ -43,8 +44,6 @@ class Ball extends CircleComponent
addAll([ addAll([
hitBox, hitBox,
]); ]);
return super.onLoad();
} }
@override @override

View File

@ -4,21 +4,22 @@ import 'dart:ui';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame/effects.dart'; import 'package:flame/effects.dart';
import 'package:flame/src/palette.dart'; import 'package:flame/src/palette.dart';
import 'package:meta/meta.dart';
/// Adds a collection of paints to a component /// Adds a collection of paints and paint layers to a component
/// ///
/// Component will always have a main Paint that can be accessed /// Component will always have a main Paint that can be accessed
/// by the [paint] attribute and other paints can be manipulated/accessed /// by the [paint] attribute and other paints can be manipulated/accessed
/// using [getPaint], [setPaint] and [deletePaint] by a paintId of generic type /// using [getPaint], [setPaint] and [deletePaint] by a paintId of generic type
/// [T], that can be omitted if the component only have one paint. /// [T], that can be omitted if the component only has one paint.
/// [paintLayers] paints should be drawn in list order during the render. The
/// main Paint is the first element.
mixin HasPaint<T extends Object> on Component implements OpacityProvider { mixin HasPaint<T extends Object> on Component implements OpacityProvider {
final Map<T, Paint> _paints = {}; late final Map<T, Paint> _paints = {};
Paint paint = BasicPalette.white.paint(); Paint paint = BasicPalette.white.paint();
void _assertGenerics() { @internal
assert(T != Object, 'A generics type is missing on the HasPaint mixin'); List<Paint>? paintLayersInternal;
}
/// Gets a paint from the collection. /// Gets a paint from the collection.
/// ///
@ -28,7 +29,6 @@ mixin HasPaint<T extends Object> on Component implements OpacityProvider {
return paint; return paint;
} }
_assertGenerics();
final _paint = _paints[paintId]; final _paint = _paints[paintId];
if (_paint == null) { if (_paint == null) {
@ -40,16 +40,29 @@ mixin HasPaint<T extends Object> on Component implements OpacityProvider {
/// Sets a paint on the collection. /// Sets a paint on the collection.
void setPaint(T paintId, Paint paint) { void setPaint(T paintId, Paint paint) {
_assertGenerics();
_paints[paintId] = paint; _paints[paintId] = paint;
} }
/// Removes a paint from the collection. /// Removes a paint from the collection.
void deletePaint(T paintId) { void deletePaint(T paintId) {
_assertGenerics();
_paints.remove(paintId); _paints.remove(paintId);
} }
/// List of paints to use (in order) during render.
List<Paint> get paintLayers {
if (!hasPaintLayers) {
return paintLayersInternal = [];
}
return paintLayersInternal!;
}
set paintLayers(List<Paint> paintLayers) {
paintLayersInternal = paintLayers;
}
/// Whether there are any paint layers defined for the component.
bool get hasPaintLayers => paintLayersInternal?.isNotEmpty ?? false;
/// Manipulate the paint to make it fully transparent. /// Manipulate the paint to make it fully transparent.
void makeTransparent({T? paintId}) { void makeTransparent({T? paintId}) {
setOpacity(0, paintId: paintId); setOpacity(0, paintId: paintId);
@ -130,9 +143,13 @@ mixin HasPaint<T extends Object> on Component implements OpacityProvider {
/// ///
/// Note: Each call results in a new [OpacityProvider] and hence the cached /// Note: Each call results in a new [OpacityProvider] and hence the cached
/// opacity ratios are calculated using opacities when this method was called. /// opacity ratios are calculated using opacities when this method was called.
OpacityProvider opacityProviderOfList({List<T?>? paintIds}) { OpacityProvider opacityProviderOfList({
List<T?>? paintIds,
bool includeLayers = true,
}) {
return _MultiPaintOpacityProvider( return _MultiPaintOpacityProvider(
paintIds ?? (List<T?>.from(_paints.keys)..add(null)), paintIds ?? (List<T?>.from(_paints.keys)..add(null)),
includeLayers,
this, this,
); );
} }
@ -152,19 +169,25 @@ class _ProxyOpacityProvider<T extends Object> implements OpacityProvider {
} }
class _MultiPaintOpacityProvider<T extends Object> implements OpacityProvider { class _MultiPaintOpacityProvider<T extends Object> implements OpacityProvider {
_MultiPaintOpacityProvider(this.paintIds, this.target) { _MultiPaintOpacityProvider(this.paintIds, this.includeLayers, this.target) {
final maxOpacity = opacity; final maxOpacity = opacity;
_opacityRatios = List<double>.generate( _opacityRatios = [
paintIds.length, for (final paintId in paintIds)
(index) => target.getOpacity(paintId: paintId) / maxOpacity,
target.getOpacity(paintId: paintIds.elementAt(index)) / maxOpacity, ];
); _layerOpacityRatios = target.paintLayersInternal
?.map(
(paint) => paint.color.opacity / maxOpacity,
)
.toList(growable: false);
} }
final List<T?> paintIds; final List<T?> paintIds;
final HasPaint<T> target; final HasPaint<T> target;
final bool includeLayers;
late final List<double> _opacityRatios; late final List<double> _opacityRatios;
late final List<double>? _layerOpacityRatios;
@override @override
double get opacity { double get opacity {
@ -173,6 +196,11 @@ class _MultiPaintOpacityProvider<T extends Object> implements OpacityProvider {
for (final paintId in paintIds) { for (final paintId in paintIds) {
maxOpacity = max(target.getOpacity(paintId: paintId), maxOpacity); maxOpacity = max(target.getOpacity(paintId: paintId), maxOpacity);
} }
if (includeLayers) {
target.paintLayersInternal?.forEach(
(paint) => maxOpacity = max(paint.color.opacity, maxOpacity),
);
}
return maxOpacity; return maxOpacity;
} }
@ -185,5 +213,13 @@ class _MultiPaintOpacityProvider<T extends Object> implements OpacityProvider {
paintId: paintIds.elementAt(i), paintId: paintIds.elementAt(i),
); );
} }
if (includeLayers) {
final paintLayersInternal = target.paintLayersInternal;
for (var i = 0; i < (paintLayersInternal?.length ?? 0); ++i) {
paintLayersInternal![i].color = paintLayersInternal[i]
.color
.withOpacity(value * _layerOpacityRatios![i]);
}
}
} }
} }

View File

@ -5,6 +5,7 @@ import 'package:flame/extensions.dart';
import 'package:flame/geometry.dart'; import 'package:flame/geometry.dart';
import 'package:flame/src/effects/provider_interfaces.dart'; import 'package:flame/src/effects/provider_interfaces.dart';
import 'package:flame/src/utils/solve_quadratic.dart'; import 'package:flame/src/utils/solve_quadratic.dart';
import 'package:meta/meta.dart';
class CircleComponent extends ShapeComponent implements SizeProvider { class CircleComponent extends ShapeComponent implements SizeProvider {
/// With this constructor you can create your [CircleComponent] from a radius /// With this constructor you can create your [CircleComponent] from a radius
@ -18,6 +19,7 @@ class CircleComponent extends ShapeComponent implements SizeProvider {
super.children, super.children,
super.priority, super.priority,
super.paint, super.paint,
super.paintLayers,
}) : super(size: Vector2.all((radius ?? 0) * 2)); }) : super(size: Vector2.all((radius ?? 0) * 2));
/// With this constructor you define the [CircleComponent] in relation to the /// With this constructor you define the [CircleComponent] in relation to the
@ -30,8 +32,19 @@ class CircleComponent extends ShapeComponent implements SizeProvider {
super.angle, super.angle,
super.anchor, super.anchor,
super.paint, super.paint,
super.paintLayers,
}) : super(size: Vector2.all(relation * min(parentSize.x, parentSize.y))); }) : super(size: Vector2.all(relation * min(parentSize.x, parentSize.y)));
@override
@mustCallSuper
Future<void> onLoad() async {
void updateCenterOffset() => _centerOffset = Offset(size.x / 2, size.y / 2);
size.addListener(updateCenterOffset);
updateCenterOffset();
}
late Offset _centerOffset;
/// Get the radius of the circle before scaling. /// Get the radius of the circle before scaling.
double get radius { double get radius {
return min(size.x, size.y) / 2; return min(size.x, size.y) / 2;
@ -56,14 +69,20 @@ class CircleComponent extends ShapeComponent implements SizeProvider {
@override @override
void render(Canvas canvas) { void render(Canvas canvas) {
if (renderShape) { if (renderShape) {
canvas.drawCircle((size / 2).toOffset(), radius, paint); if (hasPaintLayers) {
for (final paint in paintLayers) {
canvas.drawCircle(_centerOffset, radius, paint);
}
} else {
canvas.drawCircle(_centerOffset, radius, paint);
}
} }
} }
@override @override
void renderDebugMode(Canvas canvas) { void renderDebugMode(Canvas canvas) {
super.renderDebugMode(canvas); super.renderDebugMode(canvas);
canvas.drawCircle((size / 2).toOffset(), radius, debugPaint); canvas.drawCircle(_centerOffset, radius, debugPaint);
} }
/// Checks whether the represented circle contains the [point]. /// Checks whether the represented circle contains the [point].

View File

@ -38,6 +38,7 @@ class PolygonComponent extends ShapeComponent {
super.children, super.children,
super.priority, super.priority,
super.paint, super.paint,
super.paintLayers,
bool? shrinkToBounds, bool? shrinkToBounds,
}) : assert( }) : assert(
_vertices.length > 2, _vertices.length > 2,
@ -76,6 +77,7 @@ class PolygonComponent extends ShapeComponent {
Anchor? anchor, Anchor? anchor,
int? priority, int? priority,
Paint? paint, Paint? paint,
List<Paint>? paintLayers,
bool? shrinkToBounds, bool? shrinkToBounds,
}) : this( }) : this(
normalsToVertices(relation, parentSize), normalsToVertices(relation, parentSize),
@ -86,6 +88,7 @@ class PolygonComponent extends ShapeComponent {
scale: scale, scale: scale,
priority: priority, priority: priority,
paint: paint, paint: paint,
paintLayers: paintLayers,
shrinkToBounds: shrinkToBounds, shrinkToBounds: shrinkToBounds,
); );
@ -171,8 +174,14 @@ class PolygonComponent extends ShapeComponent {
@override @override
void render(Canvas canvas) { void render(Canvas canvas) {
if (renderShape) { if (renderShape) {
if (hasPaintLayers) {
for (final paint in paintLayers) {
canvas.drawPath(_path, paint); canvas.drawPath(_path, paint);
} }
} else {
canvas.drawPath(_path, paint);
}
}
} }
@override @override

View File

@ -13,6 +13,7 @@ class RectangleComponent extends PolygonComponent {
super.children, super.children,
super.priority, super.priority,
super.paint, super.paint,
super.paintLayers,
}) : super(sizeToVertices(size ?? Vector2.zero(), anchor)); }) : super(sizeToVertices(size ?? Vector2.zero(), anchor));
RectangleComponent.square({ RectangleComponent.square({
@ -22,6 +23,7 @@ class RectangleComponent extends PolygonComponent {
super.anchor, super.anchor,
super.priority, super.priority,
super.paint, super.paint,
super.paintLayers,
super.children, super.children,
}) : super(sizeToVertices(Vector2.all(size), anchor)); }) : super(sizeToVertices(Vector2.all(size), anchor));
@ -38,6 +40,7 @@ class RectangleComponent extends PolygonComponent {
super.anchor, super.anchor,
super.priority, super.priority,
super.paint, super.paint,
super.paintLayers,
super.shrinkToBounds, super.shrinkToBounds,
}) : super.relative([ }) : super.relative([
relation.clone(), relation.clone(),
@ -53,6 +56,7 @@ class RectangleComponent extends PolygonComponent {
Anchor anchor = Anchor.topLeft, Anchor anchor = Anchor.topLeft,
int? priority, int? priority,
Paint? paint, Paint? paint,
List<Paint>? paintLayers,
}) { }) {
return RectangleComponent( return RectangleComponent(
position: anchor == Anchor.topLeft position: anchor == Anchor.topLeft
@ -67,6 +71,7 @@ class RectangleComponent extends PolygonComponent {
anchor: anchor, anchor: anchor,
priority: priority, priority: priority,
paint: paint, paint: paint,
paintLayers: paintLayers,
); );
} }

View File

@ -16,8 +16,14 @@ abstract class ShapeComponent extends PositionComponent with HasPaint {
super.children, super.children,
super.priority, super.priority,
Paint? paint, Paint? paint,
List<Paint>? paintLayers,
}) { }) {
this.paint = paint ?? this.paint; this.paint = paint ?? this.paint;
// Only read from this.paintLayers if paintLayers not null to prevent
// unnecessary creation of the paintLayers list.
if (paintLayers != null) {
this.paintLayers = paintLayers;
}
} }
bool renderShape = true; bool renderShape = true;

View File

@ -1,77 +1,113 @@
import 'dart:ui'; import 'dart:ui';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
group('HasPaint', () { group('HasPaint', () {
test('paint returns the default paint', () { test('paint returns the default paint', () {
final comp = _MyComponent(); final component = _MyComponent();
expect(comp.paint, comp.getPaint()); expect(component.paint, component.getPaint());
}); });
test( test(
'paint setter sets the main paint', 'paint setter sets the main paint',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
const color = Color(0xFFE5E5E5); const color = Color(0xFFE5E5E5);
comp.paint = Paint()..color = color; component.paint = Paint()..color = color;
expect(comp.getPaint().color, color); expect(component.getPaint().color, color);
}, },
); );
test( test('paintLayers defaults to empty list', () {
'getPaint throws exception when retrieving a paint that does not exists', final component = _MyComponent();
() {
final comp = _MyComponentWithType(); const color = Color(0xFFE5E5E5);
component.paint = Paint()..color = color;
expect( expect(
() => comp.getPaint(_MyComponentKeys.background), component.paintLayers,
equals(<Paint>[]),
);
});
test('paintLayers returns correct colors', () {
const firstColor = Color(0xFFE5E5E5);
const secondColor = Color(0xFF123456);
const thirdColor = Color(0xFFABABAB);
final firstPaint = Paint()..color = firstColor;
final secondPaint = Paint()..color = secondColor;
final thirdPaint = Paint()..color = thirdColor;
final circle = CircleComponent(
radius: 10,
paint: firstPaint,
paintLayers: [secondPaint, thirdPaint],
);
expect(
circle.paintLayers,
equals([secondPaint, thirdPaint]),
);
expect(
circle.paint,
equals(firstPaint),
);
});
test('can clear paintLayers', () {
const firstColor = Color(0xFFE5E5E5);
const secondColor = Color(0xFF123456);
const thirdColor = Color(0xFFABABAB);
final firstPaint = Paint()..color = firstColor;
final secondPaint = Paint()..color = secondColor;
final thirdPaint = Paint()..color = thirdColor;
final circle = CircleComponent(
radius: 10,
paint: firstPaint,
paintLayers: [secondPaint, thirdPaint],
);
circle.paintLayers.clear();
expect(
circle.paintLayers,
equals(<Paint>[]),
);
expect(
circle.paint,
equals(firstPaint),
);
});
test(
'getPaint throws exception when retrieving a paint that does not exist',
() {
final component = _MyComponentWithType();
expect(
() => component.getPaint(_MyComponentKeys.background),
throwsArgumentError, throwsArgumentError,
); );
}, },
); );
test(
'getPaint throws exception when used on genericless component',
() {
final comp = _MyComponent();
expect(
() => comp.getPaint(_MyComponentKeys.background),
failsAssert('A generics type is missing on the HasPaint mixin'),
);
},
);
test( test(
'setPaint sets a paint', 'setPaint sets a paint',
() { () {
final comp = _MyComponentWithType(); final component = _MyComponentWithType();
const color = Color(0xFFA9A9A9); const color = Color(0xFFA9A9A9);
comp.setPaint(_MyComponentKeys.background, Paint()..color = color); component.setPaint(_MyComponentKeys.background, Paint()..color = color);
expect(comp.getPaint(_MyComponentKeys.background).color, color);
},
);
test(
'setPaint throws exception when used on genericless component',
() {
final comp = _MyComponent();
const color = Color(0xFFA9A9A9);
expect( expect(
() => comp.setPaint( component.getPaint(_MyComponentKeys.background).color,
_MyComponentKeys.background, equals(color),
Paint()..color = color,
),
failsAssert('A generics type is missing on the HasPaint mixin'),
); );
}, },
); );
@ -79,26 +115,51 @@ void main() {
test( test(
'deletePaint removes a paint from the map', 'deletePaint removes a paint from the map',
() { () {
final comp = _MyComponentWithType(); final component = _MyComponentWithType();
comp.setPaint(_MyComponentKeys.foreground, Paint()); component.setPaint(_MyComponentKeys.foreground, Paint());
comp.deletePaint(_MyComponentKeys.foreground); component.deletePaint(_MyComponentKeys.foreground);
expect( expect(
() => comp.getPaint(_MyComponentKeys.foreground), () => component.getPaint(_MyComponentKeys.foreground),
throwsArgumentError, throwsArgumentError,
); );
}, },
); );
test( test(
'deletePaint throws exception when used on genericless component', 'append paint to paintLayers',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
const color = Color(0xFFE5E5E5);
component.paintLayers.add(Paint()..color = color);
expect(component.paintLayers[0].color, equals(color));
},
);
test(
'use setPaintLayers to set multiple paintIds in paintLayers',
() {
final component = _MyComponent();
const color = Color(0xFFE5E5E5);
const anotherColor = Color(0xFFABABAB);
const thirdColor = Color(0xFF123456);
component.setPaint('test', Paint()..color = color);
component.setPaint('anotherTest', Paint()..color = anotherColor);
component.setPaint('thirdTest', Paint()..color = thirdColor);
component.paintLayers = [
component.getPaint('thirdTest'),
component.getPaint('test'),
];
expect( expect(
() => comp.deletePaint(_MyComponentKeys.background), (component.paintLayers[0].color == thirdColor) &&
failsAssert('A generics type is missing on the HasPaint mixin'), (component.paintLayers[1].color == color),
isTrue,
); );
}, },
); );
@ -106,77 +167,86 @@ void main() {
test( test(
'makeTransparent sets opacity to 0 on the main when paintId is omitted', 'makeTransparent sets opacity to 0 on the main when paintId is omitted',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
comp.makeTransparent(); component.makeTransparent();
expect(comp.paint.color.opacity, 0); expect(component.paint.color.opacity, 0);
}, },
); );
test( test(
'makeTransparent sets opacity to 0 on informed paintId', 'makeTransparent sets opacity to 0 on informed paintId',
() { () {
final comp = _MyComponentWithType(); final component = _MyComponentWithType();
comp.setPaint(_MyComponentKeys.background, Paint()); component.setPaint(_MyComponentKeys.background, Paint());
comp.makeTransparent(paintId: _MyComponentKeys.background); component.makeTransparent(paintId: _MyComponentKeys.background);
expect(comp.getPaint(_MyComponentKeys.background).color.opacity, 0); expect(
component.getPaint(_MyComponentKeys.background).color.opacity,
0,
);
}, },
); );
test( test(
'makeOpaque sets opacity to 1 on the main when paintId is omitted', 'makeOpaque sets opacity to 1 on the main when paintId is omitted',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
comp.makeTransparent(); component.makeTransparent();
comp.makeOpaque(); component.makeOpaque();
expect(comp.paint.color.opacity, 1); expect(component.paint.color.opacity, 1);
}, },
); );
test( test(
'makeOpaque sets opacity to 1 on informed paintId', 'makeOpaque sets opacity to 1 on informed paintId',
() { () {
final comp = _MyComponentWithType(); final component = _MyComponentWithType();
comp.setPaint( component.setPaint(
_MyComponentKeys.background, _MyComponentKeys.background,
Paint()..color = const Color(0x00E5E5E5), Paint()..color = const Color(0x00E5E5E5),
); );
comp.makeOpaque(paintId: _MyComponentKeys.background); component.makeOpaque(paintId: _MyComponentKeys.background);
expect(comp.getPaint(_MyComponentKeys.background).color.opacity, 1); expect(
component.getPaint(_MyComponentKeys.background).color.opacity,
1,
);
}, },
); );
test( test(
'setOpacity sets opacity of the main when paintId is omitted', 'setOpacity sets opacity of the main when paintId is omitted',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
comp.setOpacity(0.2); component.setOpacity(0.2);
expect(comp.paint.color.opacity, 0.2); expect(component.paint.color.opacity, 0.2);
}, },
); );
test( test(
'setOpacity sets opacity of the informed paintId', 'setOpacity sets opacity of the informed paintId',
() { () {
final comp = _MyComponentWithType(); final component = _MyComponentWithType();
comp.setPaint(_MyComponentKeys.background, Paint()); component.setPaint(_MyComponentKeys.background, Paint());
comp.setOpacity(0.2, paintId: _MyComponentKeys.background); component.setOpacity(0.2, paintId: _MyComponentKeys.background);
expect(comp.getPaint(_MyComponentKeys.background).color.opacity, 0.2); expect(
component.getPaint(_MyComponentKeys.background).color.opacity,
0.2,
);
}, },
); );
test( test(
'throws error if opacity is less than 0', 'throws error if opacity is less than 0',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
expect( expect(
() => comp.setOpacity(-0.5), () => component.setOpacity(-0.5),
throwsArgumentError, throwsArgumentError,
); );
}, },
@ -185,10 +255,10 @@ void main() {
test( test(
'throws error if opacity is greater than 1', 'throws error if opacity is greater than 1',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
expect( expect(
() => comp.setOpacity(1.1), () => component.setOpacity(1.1),
throwsArgumentError, throwsArgumentError,
); );
}, },
@ -197,35 +267,35 @@ void main() {
test( test(
'setColor sets color of the main when paintId is omitted', 'setColor sets color of the main when paintId is omitted',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
const color = Color(0xFFE5E5E5); const color = Color(0xFFE5E5E5);
comp.setColor(color); component.setColor(color);
expect(comp.paint.color, color); expect(component.paint.color, color);
}, },
); );
test( test(
'setOpacity sets opacity of the informed paintId', 'setOpacity sets opacity of the informed paintId',
() { () {
final comp = _MyComponentWithType(); final component = _MyComponentWithType();
const color = Color(0xFFE5E5E5); const color = Color(0xFFE5E5E5);
comp.setPaint(_MyComponentKeys.background, Paint()); component.setPaint(_MyComponentKeys.background, Paint());
comp.setColor(color, paintId: _MyComponentKeys.background); component.setColor(color, paintId: _MyComponentKeys.background);
expect(comp.getPaint(_MyComponentKeys.background).color, color); expect(component.getPaint(_MyComponentKeys.background).color, color);
}, },
); );
test( test(
'tint sets the correct color filter of the main when paintId is omitted', 'tint sets the correct color filter of the main when paintId is omitted',
() { () {
final comp = _MyComponent(); final component = _MyComponent();
const color = Color(0xFFE5E5E5); const color = Color(0xFFE5E5E5);
comp.tint(color); component.tint(color);
expect( expect(
comp.paint.colorFilter, component.paint.colorFilter,
const ColorFilter.mode(color, BlendMode.srcATop), const ColorFilter.mode(color, BlendMode.srcATop),
); );
}, },
@ -234,13 +304,13 @@ void main() {
test( test(
'setOpacity sets opacity of the informed paintId', 'setOpacity sets opacity of the informed paintId',
() { () {
final comp = _MyComponentWithType(); final component = _MyComponentWithType();
const color = Color(0xFFE5E5E5); const color = Color(0xFFE5E5E5);
comp.setPaint(_MyComponentKeys.background, Paint()); component.setPaint(_MyComponentKeys.background, Paint());
comp.tint(color, paintId: _MyComponentKeys.background); component.tint(color, paintId: _MyComponentKeys.background);
expect( expect(
comp.getPaint(_MyComponentKeys.background).colorFilter, component.getPaint(_MyComponentKeys.background).colorFilter,
const ColorFilter.mode(color, BlendMode.srcATop), const ColorFilter.mode(color, BlendMode.srcATop),
); );
}, },