feat: character selection for desktop and mobile (#280)

pull/281/head
Jorge Coca 3 years ago committed by GitHub
parent 97193c63f3
commit 3fbb2564cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -22,24 +22,9 @@ class PlayButtonOverlay extends StatelessWidget {
return Center(
child: ElevatedButton(
onPressed: () {
onPressed: () async {
_game.gameFlowController.start();
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) {
// TODO(arturplaczek): remove after merge StarBlocListener
final height = MediaQuery.of(context).size.height * 0.5;
return Center(
child: SizedBox(
height: height,
width: height * 1.4,
child: const CharacterSelectionDialog(),
),
);
},
);
await showCharacterSelectionDialog(context);
},
child: Text(l10n.play),
),

@ -46,19 +46,19 @@
},
"select": "Select",
"@select": {
"description": "Text displayed on the character selection page select button"
"description": "Text displayed on the character selection dialog - select button"
},
"space": "Space",
"@space": {
"description": "Text displayed on space control button"
},
"characterSelectionTitle": "Choose your character!",
"characterSelectionTitle": "Select a Character",
"@characterSelectionTitle": {
"description": "Title text displayed on the character selection page"
"description": "Title text displayed on the character selection dialog"
},
"characterSelectionSubtitle": "Theres no wrong answer",
"characterSelectionSubtitle": "Theres no wrong choice!",
"@characterSelectionSubtitle": {
"description": "Text displayed on the selecting character dialog at game beginning"
"description": "Subtitle text displayed on the character selection dialog"
},
"gameOver": "Game Over",
"@gameOver": {

@ -10,6 +10,14 @@ class CharacterThemeState extends Equatable {
final CharacterTheme characterTheme;
bool get isSparkySelected => characterTheme == const SparkyTheme();
bool get isDashSelected => characterTheme == const DashTheme();
bool get isAndroidSelected => characterTheme == const AndroidTheme();
bool get isDinoSelected => characterTheme == const DinoTheme();
@override
List<Object> get props => [characterTheme];
}

@ -1,139 +1,160 @@
// ignore_for_file: public_member_api_docs
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/select_character/cubit/character_theme_cubit.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball/start_game/start_game.dart';
import 'package:pinball_theme/pinball_theme.dart';
import 'package:pinball_ui/pinball_ui.dart';
class CharacterSelectionDialog extends StatelessWidget {
const CharacterSelectionDialog({Key? key}) : super(key: key);
static Route route() {
return MaterialPageRoute<void>(
/// Inflates [CharacterSelectionDialog] using [showDialog].
Future<void> showCharacterSelectionDialog(BuildContext context) {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => const CharacterSelectionDialog(),
);
}
}
/// {@template character_selection_dialog}
/// Dialog used to select the playing character of the game.
/// {@endtemplate character_selection_dialog}
class CharacterSelectionDialog extends StatelessWidget {
/// {@macro character_selection_dialog}
const CharacterSelectionDialog({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CharacterThemeCubit(),
child: const CharacterSelectionView(),
final l10n = context.l10n;
return PinballDialog(
title: l10n.characterSelectionTitle,
subtitle: l10n.characterSelectionSubtitle,
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Row(
children: [
Expanded(child: _CharacterPreview()),
Expanded(child: _CharacterGrid()),
],
),
),
const SizedBox(height: 8),
const _SelectCharacterButton(),
],
),
),
);
}
}
class CharacterSelectionView extends StatelessWidget {
const CharacterSelectionView({Key? key}) : super(key: key);
class _SelectCharacterButton extends StatelessWidget {
const _SelectCharacterButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return PinballButton(
onTap: () async {
Navigator.of(context).pop();
await showHowToPlayDialog(context);
},
text: l10n.select,
);
}
}
return PixelatedDecoration(
header: Text(
l10n.characterSelectionTitle,
style: Theme.of(context).textTheme.headline3,
),
body: SingleChildScrollView(
child: Column(
class _CharacterGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<CharacterThemeCubit, CharacterThemeState>(
builder: (context, state) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const _CharacterSelectionGridView(),
const SizedBox(height: 20),
TextButton(
onPressed: () {
Navigator.of(context).pop();
// TODO(arturplaczek): remove after merge StarBlocListener
final height = MediaQuery.of(context).size.height * 0.5;
showDialog<void>(
context: context,
builder: (_) => Center(
child: SizedBox(
height: height,
width: height * 1.4,
child: HowToPlayDialog(),
),
Column(
children: [
_Character(
key: const Key('sparky_character_selection'),
character: const SparkyTheme(),
isSelected: state.isSparkySelected,
),
);
},
child: Text(l10n.start),
const SizedBox(height: 6),
_Character(
key: const Key('android_character_selection'),
character: const AndroidTheme(),
isSelected: state.isAndroidSelected,
),
],
),
const SizedBox(width: 6),
Column(
children: [
_Character(
key: const Key('dash_character_selection'),
character: const DashTheme(),
isSelected: state.isDashSelected,
),
const SizedBox(height: 6),
_Character(
key: const Key('dino_character_selection'),
character: const DinoTheme(),
isSelected: state.isDinoSelected,
),
],
),
],
);
},
);
}
}
class _CharacterSelectionGridView extends StatelessWidget {
const _CharacterSelectionGridView({Key? key}) : super(key: key);
class _CharacterPreview extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20),
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
children: const [
CharacterImageButton(
DashTheme(),
key: Key('characterSelectionPage_dashButton'),
),
CharacterImageButton(
SparkyTheme(),
key: Key('characterSelectionPage_sparkyButton'),
),
CharacterImageButton(
AndroidTheme(),
key: Key('characterSelectionPage_androidButton'),
),
CharacterImageButton(
DinoTheme(),
key: Key('characterSelectionPage_dinoButton'),
return BlocBuilder<CharacterThemeCubit, CharacterThemeState>(
builder: (context, state) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
state.characterTheme.name,
style: Theme.of(context).textTheme.headline2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
Expanded(child: state.characterTheme.icon.image()),
],
),
);
},
);
}
}
// TODO(allisonryan0002): remove visibility when adding final UI.
@visibleForTesting
class CharacterImageButton extends StatelessWidget {
const CharacterImageButton(
this.characterTheme, {
class _Character extends StatelessWidget {
const _Character({
Key? key,
required this.character,
required this.isSelected,
}) : super(key: key);
final CharacterTheme characterTheme;
final CharacterTheme character;
final bool isSelected;
@override
Widget build(BuildContext context) {
final currentCharacterTheme =
context.select<CharacterThemeCubit, CharacterTheme>(
(cubit) => cubit.state.characterTheme,
);
return GestureDetector(
return Expanded(
child: Opacity(
opacity: isSelected ? 1 : 0.3,
child: InkWell(
onTap: () =>
context.read<CharacterThemeCubit>().characterSelected(characterTheme),
child: DecoratedBox(
decoration: BoxDecoration(
color: (currentCharacterTheme == characterTheme)
? Colors.blue.withOpacity(0.5)
: null,
borderRadius: BorderRadius.circular(6),
),
child: Padding(
padding: const EdgeInsets.all(8),
child: characterTheme.icon.image(),
context.read<CharacterThemeCubit>().characterSelected(character),
child: character.icon.image(fit: BoxFit.contain),
),
),
);

@ -50,6 +50,22 @@ extension on Control {
}
}
Future<void> showHowToPlayDialog(BuildContext context) {
final height = MediaQuery.of(context).size.height * 0.5;
return showDialog<void>(
context: context,
builder: (context) {
return Center(
child: SizedBox(
height: height,
width: height * 1.4,
child: HowToPlayDialog(),
),
);
},
);
}
class HowToPlayDialog extends StatefulWidget {
HowToPlayDialog({
Key? key,
@ -70,7 +86,7 @@ class _HowToPlayDialogState extends State<HowToPlayDialog> {
super.initState();
closeTimer = Timer(const Duration(seconds: 3), () {
if (mounted) {
Navigator.of(context).maybePop();
Navigator.of(context).pop();
}
});
}
@ -121,7 +137,10 @@ class _MobileLaunchControls extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final textStyle = Theme.of(context).textTheme.headline3;
final textStyle = Theme.of(context)
.textTheme
.headline3!
.copyWith(color: PinballColors.white);
return Column(
children: [
Text(
@ -137,7 +156,7 @@ class _MobileLaunchControls extends StatelessWidget {
),
TextSpan(
text: l10n.launch,
style: textStyle?.copyWith(color: PinballColors.blue),
style: textStyle.copyWith(color: PinballColors.blue),
),
],
),
@ -153,7 +172,10 @@ class _MobileFlipperControls extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final textStyle = Theme.of(context).textTheme.headline3;
final textStyle = Theme.of(context)
.textTheme
.headline3!
.copyWith(color: PinballColors.white);
return Column(
children: [
Text(
@ -169,7 +191,7 @@ class _MobileFlipperControls extends StatelessWidget {
),
TextSpan(
text: l10n.flip,
style: textStyle?.copyWith(color: PinballColors.orange),
style: textStyle.copyWith(color: PinballColors.orange),
),
],
),

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -10,9 +10,18 @@ import 'package:flutter/widgets.dart';
class $AssetsImagesGen {
const $AssetsImagesGen();
$AssetsImagesButtonGen get button => const $AssetsImagesButtonGen();
$AssetsImagesDialogGen get dialog => const $AssetsImagesDialogGen();
}
class $AssetsImagesButtonGen {
const $AssetsImagesButtonGen();
/// File path: assets/images/button/pinball_button.png
AssetGenImage get pinballButton =>
const AssetGenImage('assets/images/button/pinball_button.png');
}
class $AssetsImagesDialogGen {
const $AssetsImagesDialogGen();

@ -6,3 +6,4 @@ export 'package:url_launcher_platform_interface/url_launcher_platform_interface.
export 'src/dialog/dialog.dart';
export 'src/external_links/external_links.dart';
export 'src/theme/theme.dart';
export 'src/widgets/widgets.dart';

@ -1 +1,2 @@
export 'pinball_dialog.dart';
export 'pixelated_decoration.dart';

@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:pinball_ui/pinball_ui.dart';
/// {@template pinball_dialog}
/// Pinball-themed dialog.
/// {@endtemplate}
class PinballDialog extends StatelessWidget {
/// {@macro pinball_dialog}
const PinballDialog({
Key? key,
required this.title,
required this.child,
this.subtitle,
}) : super(key: key);
/// Title shown in the dialog.
final String title;
/// Optional subtitle shown below the [title].
final String? subtitle;
/// Body of the dialog.
final Widget child;
@override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height * 0.5;
return Center(
child: SizedBox(
height: height,
width: height * 1.4,
child: PixelatedDecoration(
header: subtitle != null
? _TitleAndSubtitle(title: title, subtitle: subtitle!)
: _Title(title: title),
body: child,
),
),
);
}
}
class _Title extends StatelessWidget {
const _Title({Key? key, required this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) => Text(
title,
style: Theme.of(context).textTheme.headline3!.copyWith(
fontWeight: FontWeight.bold,
color: PinballColors.darkBlue,
),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
);
}
class _TitleAndSubtitle extends StatelessWidget {
const _TitleAndSubtitle({
Key? key,
required this.title,
required this.subtitle,
}) : super(key: key);
final String title;
final String subtitle;
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
_Title(title: title),
Text(
subtitle,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: textTheme.headline3!.copyWith(fontWeight: FontWeight.normal),
),
],
);
}
}

@ -19,10 +19,11 @@ abstract class PinballTextStyle {
fontSize: 24,
package: _fontPackage,
fontFamily: _primaryFontFamily,
color: PinballColors.white,
);
static const headline3 = TextStyle(
color: PinballColors.white,
color: PinballColors.darkBlue,
fontSize: 20,
package: _fontPackage,
fontFamily: _primaryFontFamily,

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:pinball_ui/gen/gen.dart';
import 'package:pinball_ui/pinball_ui.dart';
/// {@template pinball_button}
/// Pinball-themed button with pixel art.
/// {@endtemplate}
class PinballButton extends StatelessWidget {
/// {@macro pinball_button}
const PinballButton({
Key? key,
required this.text,
required this.onTap,
}) : super(key: key);
/// Text of the button.
final String text;
/// Tap callback.
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(Assets.images.button.pinballButton.keyName),
),
),
child: Center(
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
child: Text(
text,
style: Theme.of(context)
.textTheme
.headline3!
.copyWith(color: PinballColors.white),
),
),
),
),
);
}
}

@ -0,0 +1 @@
export 'pinball_button.dart';

@ -23,6 +23,7 @@ flutter:
generate: true
assets:
- assets/images/dialog/
- assets/images/button/
fonts:
- family: PixeloidSans
fonts:

@ -0,0 +1,44 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_ui/pinball_ui.dart';
void main() {
group('PinballDialog', () {
group('with title only', () {
testWidgets('renders the title and the body', (tester) async {
tester.binding.window.physicalSizeTestValue = const Size(2000, 4000);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(
const MaterialApp(
home: PinballDialog(title: 'title', child: Placeholder()),
),
);
expect(find.byType(PixelatedDecoration), findsOneWidget);
expect(find.text('title'), findsOneWidget);
expect(find.byType(Placeholder), findsOneWidget);
});
});
group('with title and subtitle', () {
testWidgets('renders the title, subtitle and the body', (tester) async {
tester.binding.window.physicalSizeTestValue = const Size(2000, 4000);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(
MaterialApp(
home: PinballDialog(
title: 'title',
subtitle: 'sub',
child: Icon(Icons.home),
),
),
);
expect(find.byType(PixelatedDecoration), findsOneWidget);
expect(find.text('title'), findsOneWidget);
expect(find.text('sub'), findsOneWidget);
expect(find.byType(Icon), findsOneWidget);
});
});
});
}

@ -14,10 +14,10 @@ void main() {
expect(style.fontSize, 24);
});
test('headline3 has fontSize 20 and white color', () {
test('headline3 has fontSize 20 and dark blue color', () {
const style = PinballTextStyle.headline3;
expect(style.fontSize, 20);
expect(style.color, PinballColors.white);
expect(style.color, PinballColors.darkBlue);
});
test('headline4 has fontSize 16 and white color', () {

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_ui/pinball_ui.dart';
void main() {
group('PinballButton', () {
testWidgets('renders the given text and responds to taps', (tester) async {
var wasTapped = false;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: PinballButton(
text: 'test',
onTap: () {
wasTapped = true;
},
),
),
),
),
);
await tester.tap(find.text('test'));
expect(wasTapped, isTrue);
});
});
}

@ -1,3 +1,4 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart';
@ -9,37 +10,46 @@ void main() {
group('PlayButtonOverlay', () {
late PinballGame game;
late GameFlowController gameFlowController;
late CharacterThemeCubit characterThemeCubit;
setUp(() {
game = MockPinballGame();
gameFlowController = MockGameFlowController();
characterThemeCubit = MockCharacterThemeCubit();
whenListen(
characterThemeCubit,
const Stream<CharacterThemeState>.empty(),
initialState: const CharacterThemeState.initial(),
);
when(() => characterThemeCubit.state)
.thenReturn(const CharacterThemeState.initial());
when(() => game.gameFlowController).thenReturn(gameFlowController);
when(gameFlowController.start).thenAnswer((_) {});
});
testWidgets('renders correctly', (tester) async {
await tester.pumpApp(PlayButtonOverlay(game: game));
expect(find.text('Play'), findsOneWidget);
});
testWidgets('calls gameFlowController.start when taped', (tester) async {
await tester.pumpApp(PlayButtonOverlay(game: game));
testWidgets('calls gameFlowController.start when tapped', (tester) async {
await tester.pumpApp(
PlayButtonOverlay(game: game),
characterThemeCubit: characterThemeCubit,
);
await tester.tap(find.text('Play'));
await tester.pump();
verify(gameFlowController.start).called(1);
});
testWidgets('displays CharacterSelectionDialog when tapped',
(tester) async {
await tester.pumpApp(PlayButtonOverlay(game: game));
await tester.pumpApp(
PlayButtonOverlay(game: game),
characterThemeCubit: characterThemeCubit,
);
await tester.tap(find.text('Play'));
await tester.pump();
await tester.pumpAndSettle();
expect(find.byType(CharacterSelectionDialog), findsOneWidget);
});
});

@ -1,14 +1,11 @@
// ignore_for_file: prefer_const_constructors
import 'dart:async';
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockingjay/mockingjay.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball/start_game/start_game.dart';
import 'package:pinball_theme/pinball_theme.dart';
import 'package:pinball_ui/pinball_ui.dart';
import '../../helpers/helpers.dart';
@ -22,141 +19,54 @@ void main() {
const Stream<CharacterThemeState>.empty(),
initialState: const CharacterThemeState.initial(),
);
when(() => characterThemeCubit.state)
.thenReturn(const CharacterThemeState.initial());
});
group('CharacterSelectionPage', () {
testWidgets('renders CharacterSelectionView', (tester) async {
await tester.pumpApp(
CharacterSelectionDialog(),
characterThemeCubit: characterThemeCubit,
);
expect(find.byType(CharacterSelectionView), findsOneWidget);
});
testWidgets('route returns a valid navigation route', (tester) async {
group('CharacterSelectionDialog', () {
group('showCharacterSelectionDialog', () {
testWidgets('inflates the dialog', (tester) async {
await tester.pumpApp(
Scaffold(
body: Builder(
Builder(
builder: (context) {
return ElevatedButton(
onPressed: () {
Navigator.of(context)
.push<void>(CharacterSelectionDialog.route());
},
child: Text('Tap me'),
return TextButton(
onPressed: () => showCharacterSelectionDialog(context),
child: const Text('test'),
);
},
),
),
characterThemeCubit: characterThemeCubit,
);
await tester.tap(find.text('Tap me'));
await tester.tap(find.text('test'));
await tester.pumpAndSettle();
expect(find.byType(CharacterSelectionDialog), findsOneWidget);
});
});
group('CharacterSelectionView', () {
testWidgets('renders correctly', (tester) async {
const titleText = 'Choose your character!';
await tester.pumpApp(
CharacterSelectionView(),
characterThemeCubit: characterThemeCubit,
);
expect(find.text(titleText), findsOneWidget);
expect(find.byType(CharacterImageButton), findsNWidgets(4));
expect(find.byType(TextButton), findsOneWidget);
});
testWidgets('calls characterSelected when a character image is tapped',
(tester) async {
const sparkyButtonKey = Key('characterSelectionPage_sparkyButton');
await tester.pumpApp(
CharacterSelectionView(),
characterThemeCubit: characterThemeCubit,
);
await tester.tap(find.byKey(sparkyButtonKey));
verify(() => characterThemeCubit.characterSelected(SparkyTheme()))
.called(1);
});
group('HowToPlayDialog', () {
testWidgets(
'is displayed for 3 seconds when start is tapped',
testWidgets('selecting a new character calls characterSelected on cubit',
(tester) async {
await tester.pumpApp(
Scaffold(
body: Builder(
builder: (context) {
return ElevatedButton(
onPressed: () {
Navigator.of(context)
.push<void>(CharacterSelectionDialog.route());
},
child: Text('Tap me'),
);
},
),
),
const CharacterSelectionDialog(),
characterThemeCubit: characterThemeCubit,
);
await tester.tap(find.text('Tap me'));
await tester.pumpAndSettle();
await tester.ensureVisible(find.byType(TextButton));
await tester.tap(find.byType(TextButton));
await tester.pumpAndSettle();
expect(find.byType(HowToPlayDialog), findsOneWidget);
await tester.pump(Duration(seconds: 3));
await tester.tap(find.byKey(const Key('sparky_character_selection')));
await tester.pumpAndSettle();
expect(find.byType(HowToPlayDialog), findsNothing);
},
);
verify(
() => characterThemeCubit.characterSelected(const SparkyTheme()),
).called(1);
});
testWidgets(
'can be dismissed manually before 3 seconds have passed',
(tester) async {
'tapping the select button dismisses the character '
'dialog and shows the how to play dialog', (tester) async {
await tester.pumpApp(
Scaffold(
body: Builder(
builder: (context) {
return ElevatedButton(
onPressed: () {
Navigator.of(context)
.push<void>(CharacterSelectionDialog.route());
},
child: Text('Tap me'),
);
},
),
),
const CharacterSelectionDialog(),
characterThemeCubit: characterThemeCubit,
);
await tester.tap(find.text('Tap me'));
await tester.pumpAndSettle();
await tester.ensureVisible(find.byType(TextButton));
await tester.tap(find.byType(TextButton));
await tester.tap(find.byType(PinballButton));
await tester.pumpAndSettle();
expect(find.byType(CharacterSelectionDialog), findsNothing);
expect(find.byType(HowToPlayDialog), findsOneWidget);
await tester.tapAt(Offset(1, 1));
await tester.pumpAndSettle();
expect(find.byType(HowToPlayDialog), findsNothing);
},
);
});
});
testWidgets('CharacterImageButton renders correctly', (tester) async {
await tester.pumpApp(
CharacterImageButton(DashTheme()),
characterThemeCubit: characterThemeCubit,
);
expect(find.byType(Image), findsOneWidget);
});
}

@ -55,14 +55,31 @@ void main() {
expect(find.text(l10n.tapAndHoldRocket), findsOneWidget);
expect(find.text(l10n.tapLeftRightScreen), findsOneWidget);
});
});
group('KeyButton', () {
testWidgets('renders correctly', (tester) async {
testWidgets('disappears after 3 seconds', (tester) async {
await tester.pumpApp(
KeyButton(control: Control.a),
Builder(
builder: (context) {
return TextButton(
onPressed: () => showHowToPlayDialog(context),
child: const Text('test'),
);
},
),
);
expect(find.byType(HowToPlayDialog), findsNothing);
await tester.tap(find.text('test'));
await tester.pumpAndSettle();
expect(find.byType(HowToPlayDialog), findsOneWidget);
await tester.pump(const Duration(seconds: 4));
await tester.pumpAndSettle();
expect(find.byType(HowToPlayDialog), findsNothing);
});
});
group('KeyButton', () {
testWidgets('renders correctly', (tester) async {
await tester.pumpApp(KeyButton(control: Control.a));
expect(find.text('A'), findsOneWidget);
});
});

Loading…
Cancel
Save