feat: add character icon next to initials input (#209)

* chore: add icon assets

* test: update theme tests

* feat: add icon to initials input

* fix: remove material design from theme package

* refactor: suggestions

* refactor: load from cache

* chore: remove unawaited

* test: update test for caching
pull/216/head
Allison Ryan 3 years ago committed by GitHub
parent ec52486ece
commit 0bc597d34f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -32,6 +32,8 @@ class GameFlowController extends ComponentController<PinballGame>
// next page
component.firstChild<Backboard>()?.gameOverMode(
score: state?.score ?? 0,
characterIconPath:
component.theme.characterTheme.leaderboardIcon.keyName,
);
component.firstChild<CameraController>()?.focusOnBackboard();
}

@ -1,11 +1,17 @@
import 'package:pinball/game/game.dart';
import 'package:pinball/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' as components;
import 'package:pinball_theme/pinball_theme.dart' hide Assets;
/// Add methods to help loading and caching game assets.
extension PinballGameAssetsX on PinballGame {
/// Returns a list of assets to be loaded
List<Future> preLoadAssets() {
const dashTheme = DashTheme();
const sparkyTheme = SparkyTheme();
const androidTheme = AndroidTheme();
const dinoTheme = DinoTheme();
return [
images.load(components.Assets.images.ball.ball.keyName),
images.load(components.Assets.images.ball.flameEffect.keyName),
@ -93,6 +99,10 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.googleWord.letter5.keyName),
images.load(components.Assets.images.googleWord.letter6.keyName),
images.load(components.Assets.images.backboard.display.keyName),
images.load(dashTheme.leaderboardIcon.keyName),
images.load(sparkyTheme.leaderboardIcon.keyName),
images.load(androidTheme.leaderboardIcon.keyName),
images.load(dinoTheme.leaderboardIcon.keyName),
images.load(Assets.images.components.background.path),
];
}

@ -1,6 +1,6 @@
import 'package:pinball_components/gen/fonts.gen.dart';
const String _fontPath = 'packages/pinball_components/';
const String _fontPath = 'packages/pinball_components';
/// Class with the fonts available on the pinball game
class PinballFonts {

@ -34,12 +34,14 @@ class Backboard extends PositionComponent with HasGameRef {
/// Returns a [Backboard] initialized in the game over mode
factory Backboard.gameOver({
required Vector2 position,
required String characterIconPath,
required int score,
required BackboardOnSubmit onSubmit,
}) {
return Backboard(position: position)
..gameOverMode(
score: score,
characterIconPath: characterIconPath,
onSubmit: onSubmit,
);
}
@ -62,12 +64,14 @@ class Backboard extends PositionComponent with HasGameRef {
/// Puts the Backboard in game over mode, where the score input is shown.
Future<void> gameOverMode({
required int score,
required String characterIconPath,
BackboardOnSubmit? onSubmit,
}) async {
children.removeWhere((_) => true);
await add(
BackboardGameOver(
score: score,
characterIconPath: characterIconPath,
onSubmit: onSubmit,
),
);

@ -18,71 +18,35 @@ class BackboardGameOver extends PositionComponent with HasGameRef {
/// {@macro backboard_game_over}
BackboardGameOver({
required int score,
required String characterIconPath,
BackboardOnSubmit? onSubmit,
}) : _score = score,
_onSubmit = onSubmit;
}) : _onSubmit = onSubmit,
super(
children: [
_BackboardSpriteComponent(),
_BackboardDisplaySpriteComponent(),
_ScoreTextComponent(score.formatScore()),
_CharacterIconSpriteComponent(characterIconPath),
],
);
final int _score;
final BackboardOnSubmit? _onSubmit;
@override
Future<void> onLoad() async {
final backgroundSprite = await gameRef.loadSprite(
Assets.images.backboard.backboardGameOver.keyName,
);
unawaited(
add(
SpriteComponent(
sprite: backgroundSprite,
size: backgroundSprite.originalSize / 10,
anchor: Anchor.bottomCenter,
),
),
);
final displaySprite = await gameRef.loadSprite(
Assets.images.backboard.display.keyName,
);
unawaited(
add(
SpriteComponent(
sprite: displaySprite,
size: displaySprite.originalSize / 10,
anchor: Anchor.bottomCenter,
position: Vector2(0, -11.5),
),
),
);
unawaited(
add(
TextComponent(
text: _score.formatScore(),
position: Vector2(-22, -46.5),
anchor: Anchor.center,
textRenderer: Backboard.textPaint,
),
),
);
for (var i = 0; i < 3; i++) {
unawaited(
add(
await add(
BackboardLetterPrompt(
position: Vector2(
20 + (6 * i).toDouble(),
-46.5,
24.3 + (4.5 * i),
-45,
),
hasFocus: i == 0,
),
),
);
}
unawaited(
add(
await add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.arrowLeft: () => _movePrompt(true),
@ -90,7 +54,6 @@ class BackboardGameOver extends PositionComponent with HasGameRef {
LogicalKeyboardKey.enter: _submit,
},
),
),
);
}
@ -118,3 +81,64 @@ class BackboardGameOver extends PositionComponent with HasGameRef {
return false;
}
}
class _BackboardSpriteComponent extends SpriteComponent with HasGameRef {
_BackboardSpriteComponent() : super(anchor: Anchor.bottomCenter);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.backboard.backboardGameOver.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}
class _BackboardDisplaySpriteComponent extends SpriteComponent with HasGameRef {
_BackboardDisplaySpriteComponent()
: super(
anchor: Anchor.bottomCenter,
position: Vector2(0, -11.5),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.backboard.display.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}
class _ScoreTextComponent extends TextComponent {
_ScoreTextComponent(String score)
: super(
text: score,
anchor: Anchor.centerLeft,
position: Vector2(-34, -45),
textRenderer: Backboard.textPaint,
);
}
class _CharacterIconSpriteComponent extends SpriteComponent with HasGameRef {
_CharacterIconSpriteComponent(String characterIconPath)
: _characterIconPath = characterIconPath,
super(
anchor: Anchor.center,
position: Vector2(18.4, -45),
);
final String _characterIconPath;
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = Sprite(gameRef.images.fromCache(_characterIconPath));
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}

@ -34,22 +34,19 @@ class BackboardLetterPrompt extends PositionComponent {
@override
Future<void> onLoad() async {
_underscore = RectangleComponent(
size: Vector2(
4,
1.2,
),
size: Vector2(3.8, 0.8),
anchor: Anchor.center,
position: Vector2(0, 4),
position: Vector2(-0.3, 4),
);
unawaited(add(_underscore));
await add(_underscore);
_input = TextComponent(
text: 'A',
textRenderer: Backboard.textPaint,
anchor: Anchor.center,
);
unawaited(add(_input));
await add(_input);
_underscoreBlinker = TimerComponent(
period: 0.6,
@ -62,17 +59,15 @@ class BackboardLetterPrompt extends PositionComponent {
},
);
unawaited(add(_underscoreBlinker));
await add(_underscoreBlinker);
unawaited(
add(
await add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.arrowUp: () => _cycle(true),
LogicalKeyboardKey.arrowDown: () => _cycle(false),
},
),
),
);
}

@ -16,6 +16,8 @@ dependencies:
intl: ^0.17.0
pinball_flame:
path: ../pinball_flame
pinball_theme:
path: ../pinball_theme
dev_dependencies:

@ -0,0 +1,51 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_theme/pinball_theme.dart';
import 'package:sandbox/common/common.dart';
class BackboardGameOverGame extends BasicKeyboardGame {
BackboardGameOverGame(this.score, this.character);
static const info = '''
Simple example showing the game over mode of the backboard.
- Select a character to update the character icon.
''';
final int score;
final String character;
static final characterIconPaths = <String, String>{
'Dash': Assets.images.dash.leaderboardIcon.keyName,
'Sparky': Assets.images.sparky.leaderboardIcon.keyName,
'Android': Assets.images.android.leaderboardIcon.keyName,
'Dino': Assets.images.dino.leaderboardIcon.keyName,
};
@override
Future<void> onLoad() async {
camera
..followVector2(Vector2.zero())
..zoom = 5;
await images.loadAll(characterIconPaths.values.toList());
await add(
Backboard.gameOver(
position: Vector2(0, 20),
score: score,
characterIconPath: characterIconPaths[character]!,
onSubmit: (initials) {
add(
ScoreText(
text: 'User $initials made $score',
position: Vector2(0, 50),
color: Colors.pink,
),
);
},
),
);
}
}

@ -1,37 +0,0 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart';
class BackboardGameOverGame extends BasicKeyboardGame {
BackboardGameOverGame(this.score);
static const info = '''
Simple example showing the waiting mode of the backboard.
''';
final int score;
@override
Future<void> onLoad() async {
camera
..followVector2(Vector2.zero())
..zoom = 5;
await add(
Backboard.gameOver(
position: Vector2(0, 20),
score: score,
onSubmit: (initials) {
add(
ScoreText(
text: 'User $initials made $score',
position: Vector2(0, 50),
color: Colors.pink,
),
);
},
),
);
}
}

@ -1,8 +1,8 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/backboard/game_over.dart';
import 'package:sandbox/stories/backboard/waiting.dart';
import 'package:sandbox/stories/backboard/backboard_game_over_game.dart';
import 'package:sandbox/stories/backboard/backboard_waiting_game.dart';
void addBackboardStories(Dashbook dashbook) {
dashbook.storiesOf('Backboard')
@ -18,7 +18,12 @@ void addBackboardStories(Dashbook dashbook) {
'Game over',
(context) => GameWidget(
game: BackboardGameOverGame(
context.numberProperty('score', 9000000000).toInt(),
context.numberProperty('Score', 9000000000).toInt(),
context.listProperty(
'Character',
BackboardGameOverGame.characterIconPaths.keys.first,
BackboardGameOverGame.characterIconPaths.keys.toList(),
),
),
),
codeLink: buildSourceLink('backboard/game_over.dart'),

@ -64,6 +64,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
equatable:
dependency: transitive
description:
name: equatable
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
fake_async:
dependency: transitive
description:
@ -247,6 +254,13 @@ packages:
relative: true
source: path
version: "1.0.0+1"
pinball_theme:
dependency: "direct main"
description:
path: "../../pinball_theme"
relative: true
source: path
version: "1.0.0+1"
platform:
dependency: transitive
description:

@ -16,6 +16,8 @@ dependencies:
path: ../
pinball_flame:
path: ../../pinball_flame
pinball_theme:
path: ../../pinball_theme
dev_dependencies:
flutter_test:

@ -17,4 +17,6 @@ class TestGame extends Forge2DGame {
}
}
class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents {}
class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents {
KeyboardTestGame([List<String>? assets]) : super(assets);
}

@ -5,13 +5,15 @@ import 'package:flame_test/flame_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_theme/pinball_theme.dart';
import '../../helpers/helpers.dart';
void main() {
group('Backboard', () {
final tester = FlameTester(KeyboardTestGame.new);
final characterIconPath = Assets.images.dash.leaderboardIcon.keyName;
final tester = FlameTester(() => KeyboardTestGame([characterIconPath]));
group('on waitingMode', () {
tester.testGameWidget(
@ -20,6 +22,7 @@ void main() {
game.camera.zoom = 2;
game.camera.followVector2(Vector2.zero());
await game.ensureAdd(Backboard.waiting(position: Vector2(0, 15)));
await tester.pump();
},
verify: (game, tester) async {
await expectLater(
@ -39,6 +42,7 @@ void main() {
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
characterIconPath: characterIconPath,
onSubmit: (_) {},
);
await game.ensureAdd(backboard);
@ -52,7 +56,6 @@ void main() {
(component) =>
component is TextComponent && component.text == '1,000',
);
expect(score, isNotNull);
},
);
@ -63,6 +66,7 @@ void main() {
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
characterIconPath: characterIconPath,
onSubmit: (_) {},
);
await game.ensureAdd(backboard);
@ -106,6 +110,7 @@ void main() {
final backboard = Backboard.gameOver(
position: Vector2(0, 15),
score: 1000,
characterIconPath: characterIconPath,
onSubmit: (value) {
submitedInitials = value;
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

@ -39,6 +39,10 @@ class $AssetsImagesAndroidGen {
/// File path: assets/images/android/icon.png
AssetGenImage get icon =>
const AssetGenImage('assets/images/android/icon.png');
/// File path: assets/images/android/leaderboard_icon.png
AssetGenImage get leaderboardIcon =>
const AssetGenImage('assets/images/android/leaderboard_icon.png');
}
class $AssetsImagesDashGen {
@ -54,6 +58,10 @@ class $AssetsImagesDashGen {
/// File path: assets/images/dash/icon.png
AssetGenImage get icon => const AssetGenImage('assets/images/dash/icon.png');
/// File path: assets/images/dash/leaderboard_icon.png
AssetGenImage get leaderboardIcon =>
const AssetGenImage('assets/images/dash/leaderboard_icon.png');
}
class $AssetsImagesDinoGen {
@ -69,6 +77,10 @@ class $AssetsImagesDinoGen {
/// File path: assets/images/dino/icon.png
AssetGenImage get icon => const AssetGenImage('assets/images/dino/icon.png');
/// File path: assets/images/dino/leaderboard_icon.png
AssetGenImage get leaderboardIcon =>
const AssetGenImage('assets/images/dino/leaderboard_icon.png');
}
class $AssetsImagesSparkyGen {
@ -85,6 +97,10 @@ class $AssetsImagesSparkyGen {
/// File path: assets/images/sparky/icon.png
AssetGenImage get icon =>
const AssetGenImage('assets/images/sparky/icon.png');
/// File path: assets/images/sparky/leaderboard_icon.png
AssetGenImage get leaderboardIcon =>
const AssetGenImage('assets/images/sparky/leaderboard_icon.png');
}
class Assets {

@ -22,4 +22,7 @@ class AndroidTheme extends CharacterTheme {
@override
AssetGenImage get icon => Assets.images.android.icon;
@override
AssetGenImage get leaderboardIcon => Assets.images.android.leaderboardIcon;
}

@ -27,6 +27,9 @@ abstract class CharacterTheme extends Equatable {
/// Icon asset.
AssetGenImage get icon;
/// Icon asset for the leaderboard.
AssetGenImage get leaderboardIcon;
@override
List<Object?> get props => [
name,
@ -34,5 +37,6 @@ abstract class CharacterTheme extends Equatable {
character,
background,
icon,
leaderboardIcon,
];
}

@ -22,4 +22,7 @@ class DashTheme extends CharacterTheme {
@override
AssetGenImage get icon => Assets.images.dash.icon;
@override
AssetGenImage get leaderboardIcon => Assets.images.dash.leaderboardIcon;
}

@ -22,4 +22,7 @@ class DinoTheme extends CharacterTheme {
@override
AssetGenImage get icon => Assets.images.dino.icon;
@override
AssetGenImage get leaderboardIcon => Assets.images.dino.leaderboardIcon;
}

@ -22,4 +22,7 @@ class SparkyTheme extends CharacterTheme {
@override
AssetGenImage get icon => Assets.images.sparky.icon;
@override
AssetGenImage get leaderboardIcon => Assets.images.sparky.leaderboardIcon;
}

@ -17,7 +17,6 @@ dev_dependencies:
very_good_analysis: ^2.4.0
flutter:
uses-material-design: true
generate: true
assets:
- assets/images/

@ -1,6 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_theme/pinball_theme.dart';
@ -13,27 +12,5 @@ void main() {
test('supports value equality', () {
expect(AndroidTheme(), equals(AndroidTheme()));
});
test('ballColor is correct', () {
expect(AndroidTheme().ballColor, equals(Colors.green));
});
test('character asset is correct', () {
expect(
AndroidTheme().character,
equals(Assets.images.android.character),
);
});
test('background asset is correct', () {
expect(
AndroidTheme().background,
equals(Assets.images.android.background),
);
});
test('icon asset is correct', () {
expect(AndroidTheme().icon, equals(Assets.images.android.icon));
});
});
}

@ -1,6 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_theme/pinball_theme.dart';
@ -13,27 +12,5 @@ void main() {
test('supports value equality', () {
expect(DashTheme(), equals(DashTheme()));
});
test('ballColor is correct', () {
expect(DashTheme().ballColor, equals(Colors.blue));
});
test('character asset is correct', () {
expect(
DashTheme().character,
equals(Assets.images.dash.character),
);
});
test('background asset is correct', () {
expect(
DashTheme().background,
equals(Assets.images.dash.background),
);
});
test('icon asset is correct', () {
expect(DashTheme().icon, equals(Assets.images.dash.icon));
});
});
}

@ -1,6 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_theme/pinball_theme.dart';
@ -13,27 +12,5 @@ void main() {
test('supports value equality', () {
expect(DinoTheme(), equals(DinoTheme()));
});
test('ballColor is correct', () {
expect(DinoTheme().ballColor, equals(Colors.grey));
});
test('character asset is correct', () {
expect(
DinoTheme().character,
equals(Assets.images.dino.character),
);
});
test('background asset is correct', () {
expect(
DinoTheme().background,
equals(Assets.images.dino.background),
);
});
test('icon asset is correct', () {
expect(DinoTheme().icon, equals(Assets.images.dino.icon));
});
});
}

@ -1,6 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_theme/pinball_theme.dart';
@ -13,27 +12,5 @@ void main() {
test('supports value equality', () {
expect(SparkyTheme(), equals(SparkyTheme()));
});
test('ballColor is correct', () {
expect(SparkyTheme().ballColor, equals(Colors.orange));
});
test('character asset is correct', () {
expect(
SparkyTheme().character,
equals(Assets.images.sparky.character),
);
});
test('background asset is correct', () {
expect(
SparkyTheme().background,
equals(Assets.images.sparky.background),
);
});
test('icon asset is correct', () {
expect(SparkyTheme().icon, equals(Assets.images.sparky.icon));
});
});
}

@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_theme/pinball_theme.dart';
import '../../helpers/helpers.dart';
@ -43,6 +44,7 @@ void main() {
when(
() => backboard.gameOverMode(
score: any(named: 'score'),
characterIconPath: any(named: 'characterIconPath'),
onSubmit: any(named: 'onSubmit'),
),
).thenAnswer((_) async {});
@ -55,6 +57,8 @@ void main() {
when(game.firstChild<Backboard>).thenReturn(backboard);
when(game.firstChild<CameraController>).thenReturn(cameraController);
when(() => game.overlays).thenReturn(overlays);
when(() => game.theme)
.thenReturn(PinballTheme(characterTheme: DashTheme()));
});
test(
@ -71,6 +75,7 @@ void main() {
verify(
() => backboard.gameOverMode(
score: 0,
characterIconPath: any(named: 'characterIconPath'),
onSubmit: any(named: 'onSubmit'),
),
).called(1);

Loading…
Cancel
Save