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 'game_hud.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