From 83f117d0b3e2ad43b1a5437b5741128405ab31c7 Mon Sep 17 00:00:00 2001 From: arturplaczek Date: Tue, 19 Apr 2022 18:53:11 +0200 Subject: [PATCH] feat: create BonusAnimation --- lib/game/view/widgets/bonus_animation.dart | 70 +++++++++ .../view/widgets/bonus_animation_test.dart | 136 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 lib/game/view/widgets/bonus_animation.dart create mode 100644 test/game/view/widgets/bonus_animation_test.dart diff --git a/lib/game/view/widgets/bonus_animation.dart b/lib/game/view/widgets/bonus_animation.dart new file mode 100644 index 00000000..06271170 --- /dev/null +++ b/lib/game/view/widgets/bonus_animation.dart @@ -0,0 +1,70 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flame/flame.dart'; +import 'package:flame/image_composition.dart'; +import 'package:flame/sprite.dart'; +import 'package:flame/widgets.dart'; +import 'package:flutter/material.dart' hide Image; +import 'package:pinball/gen/assets.gen.dart'; + +class BonusAnimation extends StatelessWidget { + const BonusAnimation._( + this.image, { + this.onCompleted, + Key? key, + }) : super(key: key); + + BonusAnimation.dashNest({ + Key? key, + VoidCallback? onCompleted, + }) : this._( + Assets.images.bonusAnimation.dashNest.keyName, + onCompleted: onCompleted, + key: key, + ); + + final String image; + + final VoidCallback? onCompleted; + + @override + Widget build(BuildContext context) { + Flame.images.prefix = ''; + + return FutureBuilder( + future: Flame.images.load(Assets.images.bonusAnimation.dashNest.keyName), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + final spriteSheet = SpriteSheet.fromColumnsAndRows( + image: snapshot.data!, + columns: 8, + rows: 9, + ); + final animation = spriteSheet.createAnimation( + row: 0, + stepTime: 1 / 24, + to: spriteSheet.rows * spriteSheet.columns, + loop: false, + ); + + Future.delayed( + Duration(seconds: animation.totalDuration().ceil()), + () { + onCompleted?.call(); + }, + ); + + return SizedBox( + width: double.infinity, + height: double.infinity, + child: SpriteAnimationWidget( + animation: animation, + ), + ); + } + + return const SizedBox(); + }, + ); + } +} diff --git a/test/game/view/widgets/bonus_animation_test.dart b/test/game/view/widgets/bonus_animation_test.dart new file mode 100644 index 00000000..152de31d --- /dev/null +++ b/test/game/view/widgets/bonus_animation_test.dart @@ -0,0 +1,136 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'dart:ui' as ui; + +import 'package:flame/assets.dart'; +import 'package:flame/flame.dart'; +import 'package:flame/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/view/widgets/bonus_animation.dart'; + +class MockImages extends Mock implements Images {} + +class MockImage extends Mock implements ui.Image {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final image = decodeImageFromList(Uint8List.fromList(transparentImage)); + late Images images; + + setUp(() { + images = MockImages(); + when(() => images.load(any())).thenAnswer((_) => image); + + Flame.images = images; + }); + + testWidgets('renders SpriteAnimationWidget', (tester) async { + await tester.runAsync(() async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: BonusAnimation.dashNest(), + ), + ), + ); + await tester.pump(); + + expect(find.byType(SpriteAnimationWidget), findsOneWidget); + }); + }); + + testWidgets('called onCompleted callback at the end of animation ', + (tester) async { + final completer = Completer(); + + await tester.runAsync(() async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: BonusAnimation.dashNest( + onCompleted: completer.complete, + ), + ), + ), + ); + + await tester.pump(); + + await Future.delayed(const Duration(seconds: 4)); + + await tester.pump(); + + expect(completer.isCompleted, isTrue); + }); + }); +} + +const transparentImage = [ + 0x89, + 0x50, + 0x4E, + 0x47, + 0x0D, + 0x0A, + 0x1A, + 0x0A, + 0x00, + 0x00, + 0x00, + 0x0D, + 0x49, + 0x48, + 0x44, + 0x52, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x01, + 0x08, + 0x06, + 0x00, + 0x00, + 0x00, + 0x1F, + 0x15, + 0xC4, + 0x89, + 0x00, + 0x00, + 0x00, + 0x0A, + 0x49, + 0x44, + 0x41, + 0x54, + 0x78, + 0x9C, + 0x63, + 0x00, + 0x01, + 0x00, + 0x00, + 0x05, + 0x00, + 0x01, + 0x0D, + 0x0A, + 0x2D, + 0xB4, + 0x00, + 0x00, + 0x00, + 0x00, + 0x49, + 0x45, + 0x4E, + 0x44, + 0xAE, +];