Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add pause menu #5

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 40 additions & 9 deletions lib/puzzle/bloc/puzzle_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import 'package:zcomponents/zcomponents.dart';
part 'puzzle_event.dart';
part 'puzzle_state.dart';

const _kTutorialDuration = Duration(seconds: 4);
const _kTutorialDuration = Duration(seconds: 3);

class PuzzleBloc extends Bloc<PuzzleEvent, PuzzleState> {
PuzzleBloc({
required PuzzlesRepository puzzlesRepository,
}) : _puzzlesRepository = puzzlesRepository,
super(PuzzleState(history: const [RushPuzzle.empty()])) {
super(PuzzleState.empty()) {
on<PuzzleFetched>(_onPuzzleFetched);
on<PuzzleStarted>(_onPuzzleStarted);
on<PuzzleReseted>(_onPuzzleReseted);
on<PuzzlePaused>(_onPuzzlePaused);
on<PuzzleResumed>(_onPuzzleResumed);
on<PuzzleVehicleMoved>(_onPuzzleVehicleMoved);
on<PuzzleMoveUndid>(_onPuzzleMoveUndid);
on<PuzzleShared>(_onPuzzleShared);
Expand Down Expand Up @@ -74,7 +76,7 @@ class PuzzleBloc extends Bloc<PuzzleEvent, PuzzleState> {
Emitter emit,
) async {
assert(
state.status == GameStatus.playing,
state.status.isPlaying,
'Vehicle should move only when playing',
);
final newPuzzle = event.vehicle.driveTo(state.puzzle, event.newPosition);
Expand All @@ -100,9 +102,12 @@ class PuzzleBloc extends Bloc<PuzzleEvent, PuzzleState> {

Future<void> _onPuzzleReseted(PuzzleReseted event, Emitter emit) async {
emit(
PuzzleState(
status: GameStatus.setup,
history: [state.history[0]],
state.copyWith(
status: GameStatus.playing,
history: [
state.history[0],
],
historyPointer: 0,
),
);
await FirebaseAnalytics.instance.logEvent(
Expand All @@ -113,6 +118,30 @@ class PuzzleBloc extends Bloc<PuzzleEvent, PuzzleState> {
);
}

Future<void> _onPuzzlePaused(PuzzlePaused event, Emitter emit) async {
emit(
state.copyWith(status: GameStatus.paused),
);
await FirebaseAnalytics.instance.logEvent(
name: 'game_paused',
parameters: {
'version': puzzleVersion,
},
);
}

Future<void> _onPuzzleResumed(PuzzleResumed event, Emitter emit) async {
emit(
state.copyWith(status: GameStatus.playing),
);
await FirebaseAnalytics.instance.logEvent(
name: 'game_resumed',
parameters: {
'version': puzzleVersion,
},
);
}

void _onPuzzleMoveUndid(PuzzleMoveUndid event, Emitter emit) {
if (!state.canUndo) return;

Expand Down Expand Up @@ -168,9 +197,11 @@ class PuzzleBloc extends Bloc<PuzzleEvent, PuzzleState> {
_PuzzleTutorialFinished _,
Emitter emit,
) async {
emit(
state.copyWith(status: GameStatus.playing),
);
if (state.status.isTutorial) {
emit(
state.copyWith(status: GameStatus.playing),
);
}
}
}

Expand Down
16 changes: 16 additions & 0 deletions lib/puzzle/bloc/puzzle_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ class PuzzleReseted extends PuzzleEvent {
List<Object?> get props => [];
}

class PuzzlePaused extends PuzzleEvent {
const PuzzlePaused();

@override
List<Object?> get props => [];
}



class PuzzleResumed extends PuzzleEvent {
const PuzzleResumed();

@override
List<Object?> get props => [];
}

class PuzzleShared extends PuzzleEvent {
const PuzzleShared();

Expand Down
26 changes: 22 additions & 4 deletions lib/puzzle/bloc/puzzle_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ enum GameStatus {
/// Game is being played.
playing,

/// Game is paused.
paused,

/// Game has been won.
finished,

Expand All @@ -29,20 +32,30 @@ extension GameStatusExtension on GameStatus {
bool get isPlaying => this == GameStatus.playing || isTutorial;

bool get isTutorial => this == GameStatus.tutorial;

bool get isPaused => this == GameStatus.paused;
}

@immutable
class PuzzleState extends Equatable {
const PuzzleState({
this.status = GameStatus.initial,
this.perspective = GameLayoutPerspective.p3D,
required this.status,
required this.perspective,
required this.history,
this.historyPointer = 0,
required this.historyPointer,
}) : assert(
0 <= historyPointer && historyPointer < history.length,
'historyPointer is invalid',
);

const PuzzleState.empty()
: this(
status: GameStatus.initial,
perspective: GameLayoutPerspective.p3D,
history: const [RushPuzzle.empty()],
historyPointer: 0,
);

final GameStatus status;

final List<RushPuzzle> history;
Expand Down Expand Up @@ -72,5 +85,10 @@ class PuzzleState extends Equatable {
}

@override
List<Object?> get props => [history, historyPointer, status, perspective];
List<Object?> get props => [
history,
historyPointer,
status,
perspective,
];
}
98 changes: 98 additions & 0 deletions lib/puzzle/view/puzzle_paused_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) 2021, Very Good Ventures
// https://verygood.ventures
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rush_hour_puzzle/puzzle/puzzle.dart';

import 'package:z_cubic_text/z_cubic_text.dart';
import 'package:zcomponents/zcomponents.dart';

class PuzzlePausedView extends StatelessWidget {
const PuzzlePausedView({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
final zStyle = ZCubicTextStyle(
fontSize: 14,
letterSpacing: 12,
frontColor: Colors.grey[800],
);
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
child: Container(
color: Colors.blue[100]!.withOpacity(0.7),
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Column(
children: [
const SizedBox(height: 160),
SizedBox(
height: 100,
width: 400,
child: ZIllustration(
children: [
ZAnimatedPositioned.position(
duration: const Duration(milliseconds: 200),
position: const ZPosition(
rotate: ZVector.only(x: -0.25, y: -0.5),
),
child: ZPositioned(
translate: const ZVector.only(x: 16),
child: ZCubicText(
'PAUSED',
style: zStyle,
),
),
),
],
),
),
const SizedBox(height: 48),
],
),
),
SliverList(
delegate: SliverChildListDelegate.fixed([
PuzzlePausedTile(
icon: const Icon(Icons.help),
title: const Text('Show Instructions'),
onPressed: () {
context.read<PuzzleBloc>().add(
const PuzzleTutorialStarted(),
);
},
),
PuzzlePausedTile(
icon: const Icon(Icons.restart_alt),
title: const Text('Restart'),
onPressed: () {
context.read<PuzzleBloc>().add(const PuzzleReseted());
},
),
const SizedBox(height: 48),
PuzzlePausedTile(
icon: const Icon(
Icons.play_arrow,
),
title: const Text('Continue'),
onPressed: () {
context.read<PuzzleBloc>().add(const PuzzleResumed());
},
),
]),
),
const SliverToBoxAdapter(child: SizedBox(height: 120))
],
),
),
);
}
}
11 changes: 7 additions & 4 deletions lib/puzzle/view/puzzle_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class PuzzleView extends StatelessWidget {
PuzzleZFinishGroup(boardTheme: boardTheme),
],
)
else if (state.status.isPlaying)
else if (state.status.isPlaying || state.status.isPaused)
ZGroup(
children: [
for (final vehicle in vehicles)
Expand All @@ -80,14 +80,17 @@ class PuzzleView extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
TutorialButton(),
SizedBox(width: 10),
PerspectiveSegmentedControl(),
SizedBox(width: 8),
PauseButton(),
],
),
),
),
]
],
if (state.status == GameStatus.paused) ...[
const PuzzlePausedView(),
],
],
);

Expand Down
1 change: 1 addition & 0 deletions lib/puzzle/view/view.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'puzzle_page.dart';
export 'puzzle_paused_view.dart';
export 'puzzle_view.dart';
export 'puzzle_z_finish_view.dart';
export 'puzzle_z_setup_view.dart';
36 changes: 36 additions & 0 deletions lib/puzzle/widgets/pause_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rush_hour_puzzle/puzzle/puzzle.dart';

const double _kCornerRadius = 8;

class PauseButton extends StatelessWidget {
const PauseButton({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
const border = BorderRadius.all(Radius.circular(_kCornerRadius));
return InkWell(
onTap: () {
context.read<PuzzleBloc>().add(const PuzzlePaused());
},
borderRadius: border,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 6),
decoration: BoxDecoration(
borderRadius: border,
color: CupertinoDynamicColor.resolve(
CupertinoColors.tertiarySystemFill,
context,
),
),
child: Icon(
Icons.settings,
size: 20,
color: Colors.grey[800],
),
),
);
}
}
12 changes: 7 additions & 5 deletions lib/puzzle/widgets/perspective_segmented_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ class PerspectiveSegmentedControl extends StatelessWidget {
fontSize: 14,
),
),
GameLayoutPerspective.p3D: Text('3D',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
),),
GameLayoutPerspective.p3D: Text(
'3D',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
},
);
}
Expand Down
Loading