mirror of
				https://github.com/flame-engine/flame.git
				synced 2025-11-01 01:18:38 +08:00 
			
		
		
		
	feat: Add paint layers to HasPaint and associated component renders (#2073)
This commit is contained in:
		| @ -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 | ||||||
|  | |||||||
| @ -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]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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]. | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | |||||||
| @ -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; | ||||||
|  | |||||||
| @ -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), | ||||||
|         ); |         ); | ||||||
|       }, |       }, | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Matt Bennett
					Matt Bennett