mirror of https://github.com/flutter/pinball.git
parent
b6cd9663b9
commit
19bfd035cc
@ -0,0 +1,59 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
import 'package:pinball/l10n/l10n.dart';
|
||||||
|
import 'package:pinball/theme/theme.dart';
|
||||||
|
|
||||||
|
class ScoreBalls extends StatelessWidget {
|
||||||
|
const ScoreBalls({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
final balls = context.select((GameBloc bloc) => bloc.state.balls);
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
l10n.ballCt,
|
||||||
|
style: AppTextStyle.subtitle1.copyWith(
|
||||||
|
color: AppColors.orange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
ScoreBall(isActive: balls >= 1),
|
||||||
|
ScoreBall(isActive: balls >= 2),
|
||||||
|
ScoreBall(isActive: balls >= 3),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
class ScoreBall extends StatelessWidget {
|
||||||
|
const ScoreBall({
|
||||||
|
Key? key,
|
||||||
|
required this.isActive,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final bool isActive;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final color = isActive ? AppColors.orange : AppColors.orange.withAlpha(128);
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: Container(
|
||||||
|
color: color,
|
||||||
|
height: 8,
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
import 'package:pinball/l10n/l10n.dart';
|
||||||
|
import 'package:pinball/theme/theme.dart';
|
||||||
|
|
||||||
|
class ScoreView extends StatelessWidget {
|
||||||
|
const ScoreView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isGameOver = context.select((GameBloc bloc) => bloc.state.isGameOver);
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: AnimatedSwitcher(
|
||||||
|
duration: kThemeAnimationDuration,
|
||||||
|
child: isGameOver ? const _GameOver() : const _ScoreWidget(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GameOver extends StatelessWidget {
|
||||||
|
const _GameOver({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
l10n.gameOver,
|
||||||
|
style: AppTextStyle.headline1.copyWith(
|
||||||
|
color: AppColors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ScoreWidget extends StatelessWidget {
|
||||||
|
const _ScoreWidget({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
l10n.score.toLowerCase(),
|
||||||
|
style: AppTextStyle.subtitle1.copyWith(
|
||||||
|
color: AppColors.orange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const _ScoreText(),
|
||||||
|
const ScoreBalls(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ScoreText extends StatelessWidget {
|
||||||
|
const _ScoreText({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final score = context.select((GameBloc bloc) => bloc.state.score);
|
||||||
|
final numberFormatter = NumberFormat.decimalPattern('en_US');
|
||||||
|
final formattedScore = numberFormatter.format(score).replaceAll(',', '.');
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
formattedScore,
|
||||||
|
style: AppTextStyle.headline1.copyWith(
|
||||||
|
color: AppColors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
export 'bonus_animation.dart';
|
export 'bonus_animation.dart';
|
||||||
export 'game_hud.dart';
|
export 'game_hud.dart';
|
||||||
export 'play_button_overlay.dart';
|
export 'play_button_overlay.dart';
|
||||||
|
export 'score_ball.dart';
|
||||||
|
export 'score_view.dart';
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
import 'package:pinball/theme/app_colors.dart';
|
||||||
|
|
||||||
|
import '../../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ScoreBalls renders', () {
|
||||||
|
late GameBloc gameBloc;
|
||||||
|
const initialState = GameState(
|
||||||
|
score: 0,
|
||||||
|
balls: 3,
|
||||||
|
bonusHistory: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
gameBloc = MockGameBloc();
|
||||||
|
|
||||||
|
whenListen(
|
||||||
|
gameBloc,
|
||||||
|
Stream.value(initialState),
|
||||||
|
initialState: initialState,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('three active balls', (tester) async {
|
||||||
|
await tester.pumpApp(
|
||||||
|
const ScoreBalls(),
|
||||||
|
gameBloc: gameBloc,
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.byType(ScoreBall), findsNWidgets(3));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('two active balls', (tester) async {
|
||||||
|
final state = initialState.copyWith(
|
||||||
|
balls: 2,
|
||||||
|
);
|
||||||
|
whenListen(
|
||||||
|
gameBloc,
|
||||||
|
Stream.value(state),
|
||||||
|
initialState: state,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpApp(
|
||||||
|
const ScoreBalls(),
|
||||||
|
gameBloc: gameBloc,
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byWidgetPredicate(
|
||||||
|
(widget) => widget is ScoreBall && widget.isActive,
|
||||||
|
),
|
||||||
|
findsNWidgets(2),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byWidgetPredicate(
|
||||||
|
(widget) => widget is ScoreBall && !widget.isActive,
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('active score ball is displaying with proper color',
|
||||||
|
(tester) async {
|
||||||
|
await tester.pumpApp(
|
||||||
|
const ScoreBall(isActive: true),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byWidgetPredicate(
|
||||||
|
(widget) => widget is Container && widget.color == AppColors.orange,
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('inactive score ball is displaying with proper color',
|
||||||
|
(tester) async {
|
||||||
|
await tester.pumpApp(
|
||||||
|
const ScoreBall(isActive: false),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byWidgetPredicate(
|
||||||
|
(widget) =>
|
||||||
|
widget is Container &&
|
||||||
|
widget.color == AppColors.orange.withAlpha(128),
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
import 'package:pinball/l10n/l10n.dart';
|
||||||
|
|
||||||
|
import '../../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late GameBloc gameBloc;
|
||||||
|
late StreamController<GameState> stateController;
|
||||||
|
const score = 123456789;
|
||||||
|
const initialState = GameState(
|
||||||
|
score: score,
|
||||||
|
balls: 1,
|
||||||
|
bonusHistory: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
gameBloc = MockGameBloc();
|
||||||
|
stateController = StreamController<GameState>()..add(initialState);
|
||||||
|
|
||||||
|
whenListen(
|
||||||
|
gameBloc,
|
||||||
|
stateController.stream,
|
||||||
|
initialState: initialState,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
String _formatScore(int score) {
|
||||||
|
final numberFormatter = NumberFormat.decimalPattern('en_US');
|
||||||
|
return numberFormatter.format(score).replaceAll(',', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
group('ScoreView', () {
|
||||||
|
testWidgets('renders score', (tester) async {
|
||||||
|
await tester.pumpApp(
|
||||||
|
const ScoreView(),
|
||||||
|
gameBloc: gameBloc,
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text(_formatScore(score)), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('renders game over', (tester) async {
|
||||||
|
final l10n = await AppLocalizations.delegate.load(const Locale('en'));
|
||||||
|
|
||||||
|
stateController.add(
|
||||||
|
initialState.copyWith(
|
||||||
|
balls: 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpApp(
|
||||||
|
const ScoreView(),
|
||||||
|
gameBloc: gameBloc,
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text(l10n.gameOver), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('updates the score', (tester) async {
|
||||||
|
await tester.pumpApp(
|
||||||
|
const ScoreView(),
|
||||||
|
gameBloc: gameBloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text('$score'), findsOneWidget);
|
||||||
|
|
||||||
|
final newState = initialState.copyWith(
|
||||||
|
score: 987654321,
|
||||||
|
);
|
||||||
|
|
||||||
|
stateController.add(newState);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text(_formatScore(newState.score)), findsOneWidget);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue