Skip to content

Commit

Permalink
feat: add puzzle completion and game over ui, minor updates
Browse files Browse the repository at this point in the history
  • Loading branch information
thisissandipp committed Jul 29, 2024
1 parent f6c38ca commit 4d543ed
Show file tree
Hide file tree
Showing 24 changed files with 878 additions and 250 deletions.
12 changes: 12 additions & 0 deletions lib/colors/colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,16 @@ abstract class SudokuColors {

/// Dark Purple
static const darkPurple = Color(0xFF7A57FD);

/// Green
static const green = Color(0xFF388E3C);

/// Amber
static const amber = Color(0xFFEBB208);

/// Orange
static const orange = Color(0xFFF57C00);

/// Teal
static const teal = Color(0xFF008577);
}
48 changes: 36 additions & 12 deletions lib/puzzle/view/puzzle_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,42 @@ class PuzzleView extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);

return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
scrolledUnderElevation: 0,
systemOverlayStyle: theme.brightness == Brightness.light
? SystemUiOverlayStyle.dark
: SystemUiOverlayStyle.light,
),
body: const SudokuBackground(
child: PuzzleViewLayout(),
return BlocListener<PuzzleBloc, PuzzleState>(
listenWhen: (p, c) => p.puzzleStatus != c.puzzleStatus,
listener: (context, state) {
if (state.puzzleStatus == PuzzleStatus.complete) {
context.read<TimerBloc>().add(const TimerStopped());
final timeInSeconds = context.read<TimerBloc>().state.secondsElapsed;
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => CongratsDialog(
difficulty: state.puzzle.difficulty,
timeInSeconds: timeInSeconds,
),
);
} else if (state.puzzleStatus == PuzzleStatus.failed) {
context.read<TimerBloc>().add(const TimerStopped());
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => const GameOverDialog(),
);
}
},
child: Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
scrolledUnderElevation: 0,
systemOverlayStyle: theme.brightness == Brightness.light
? SystemUiOverlayStyle.dark
: SystemUiOverlayStyle.light,
),
body: const SudokuBackground(
child: PuzzleViewLayout(),
),
),
);
}
Expand Down
147 changes: 147 additions & 0 deletions lib/puzzle/widgets/congrats_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import 'package:flutter/material.dart';
import 'package:sudoku/colors/colors.dart';
import 'package:sudoku/layout/layout.dart';
import 'package:sudoku/models/models.dart';
import 'package:sudoku/typography/typography.dart';
import 'package:sudoku/utilities/utilities.dart';
import 'package:sudoku/widgets/widgets.dart';

class CongratsDialog extends StatelessWidget {
const CongratsDialog({
required this.difficulty,
required this.timeInSeconds,
super.key,
});

final Difficulty difficulty;
final int timeInSeconds;

@override
Widget build(BuildContext context) {
const gradient = LinearGradient(
colors: [
SudokuColors.darkPurple,
SudokuColors.darkPink,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight,
);

return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 440,
),
child: ResponsiveLayoutBuilder(
small: (_, child) => Padding(
key: const Key('congrats_dialog_small'),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
child: child,
),
medium: (_, child) => Padding(
key: const Key('congrats_dialog_medium'),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 28),
child: child,
),
large: (_, child) => Padding(
key: const Key('congrats_dialog_large'),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
child: child,
),
child: (layoutSize) {
final gap = switch (layoutSize) {
ResponsiveLayoutSize.small => 16.0,
ResponsiveLayoutSize.medium => 18.0,
ResponsiveLayoutSize.large => 22.0,
};

final titleFontSize = switch (layoutSize) {
ResponsiveLayoutSize.small => 16.0,
ResponsiveLayoutSize.medium => 18.0,
ResponsiveLayoutSize.large => 22.0,
};

final subtitleFontSize = switch (layoutSize) {
ResponsiveLayoutSize.small => 12.0,
ResponsiveLayoutSize.medium => 14.0,
ResponsiveLayoutSize.large => 16.0,
};

return Column(
mainAxisSize: MainAxisSize.min,
children: [
ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (bounds) => gradient.createShader(
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
),
child: Text(
'Congratulations!!!',
style: SudokuTextStyle.bodyText1.copyWith(
fontWeight: SudokuFontWeight.semiBold,
fontSize: titleFontSize,
),
),
),
SizedBox(height: gap),
RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: SudokuTextStyle.caption.copyWith(
height: 1.4,
fontSize: subtitleFontSize,
color: Theme.of(context).brightness == Brightness.light
? Colors.black
: Colors.white,
),
children: [
const TextSpan(
text: 'You have finished ',
),
TextSpan(
text: '${difficulty.article} ${difficulty.name}',
style: SudokuTextStyle.caption.copyWith(
fontWeight: SudokuFontWeight.medium,
fontSize: subtitleFontSize,
color: difficulty.color,
),
),
const TextSpan(
text: ' level sudoku in ',
),
TextSpan(
text: timeInSeconds.format,
style: SudokuTextStyle.caption.copyWith(
fontWeight: SudokuFontWeight.semiBold,
),
),
],
),
),
SizedBox(height: gap),
Text(
'Thank you for playing the sudoku puzzle!',
textAlign: TextAlign.center,
style: SudokuTextStyle.caption.copyWith(
fontWeight: SudokuFontWeight.medium,
fontSize: subtitleFontSize,
),
),
SizedBox(height: gap),
SudokuElevatedButton(
buttonText: 'Return to Home Page',
onPressed: () => Navigator.of(context).popUntil(
(route) => route.isFirst,
),
),
],
);
},
),
),
);
}
}
119 changes: 119 additions & 0 deletions lib/puzzle/widgets/game_over_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:sudoku/layout/layout.dart';
import 'package:sudoku/typography/typography.dart';
import 'package:sudoku/widgets/widgets.dart';

class GameOverDialog extends StatelessWidget {
const GameOverDialog({super.key});

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);

return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 440,
),
child: ResponsiveLayoutBuilder(
small: (_, child) => Padding(
key: const Key('game_over_dialog_small'),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
child: child,
),
medium: (_, child) => Padding(
key: const Key('game_over_dialog_medium'),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 28),
child: child,
),
large: (_, child) => Padding(
key: const Key('game_over_dialog_large'),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
child: child,
),
child: (layoutSize) {
final gap = switch (layoutSize) {
ResponsiveLayoutSize.small => 16.0,
ResponsiveLayoutSize.medium => 18.0,
ResponsiveLayoutSize.large => 22.0,
};

final titleFontSize = switch (layoutSize) {
ResponsiveLayoutSize.small => 16.0,
ResponsiveLayoutSize.medium => 18.0,
ResponsiveLayoutSize.large => 22.0,
};

final subtitleFontSize = switch (layoutSize) {
ResponsiveLayoutSize.small => 12.0,
ResponsiveLayoutSize.medium => 14.0,
ResponsiveLayoutSize.large => 16.0,
};

return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Game Over!!!',
style: SudokuTextStyle.bodyText1.copyWith(
fontWeight: SudokuFontWeight.semiBold,
fontSize: titleFontSize,
color: theme.colorScheme.error,
),
),
SizedBox(height: gap),
RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: SudokuTextStyle.caption.copyWith(
height: 1.4,
fontSize: subtitleFontSize,
color: Theme.of(context).brightness == Brightness.light
? Colors.black
: Colors.white,
),
children: [
const TextSpan(
text: 'You have exhausted all of the ',
),
TextSpan(
text: '3 allowed mistakes',
style: SudokuTextStyle.caption.copyWith(
fontWeight: SudokuFontWeight.medium,
fontSize: subtitleFontSize,
),
),
const TextSpan(
text: ' in this puzzle.',
),
],
),
),
SizedBox(height: gap),
Text(
'Thank you for playing the sudoku puzzle!',
textAlign: TextAlign.center,
style: SudokuTextStyle.caption.copyWith(
fontWeight: SudokuFontWeight.medium,
fontSize: subtitleFontSize,
),
),
SizedBox(height: gap),
SudokuElevatedButton(
buttonText: 'Return to Home Page',
onPressed: () => Navigator.popUntil(
context,
(route) => route.isFirst,
),
),
],
);
},
),
),
);
}
}
2 changes: 2 additions & 0 deletions lib/puzzle/widgets/widgets.dart
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export 'congrats_dialog.dart';
export 'game_over_dialog.dart';
export 'mistakes_count_view.dart';
8 changes: 5 additions & 3 deletions lib/sudoku/widgets/sudoku_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ class SudokuBlock extends StatelessWidget {
child: DecoratedBox(
decoration: BoxDecoration(
color: isBlockSelected
? theme.primaryColorLight
? SudokuColors.lightPurple.withOpacity(0.27)
: isBlockHighlighted
? theme.splashColor.withOpacity(0.27)
? theme.splashColor.withOpacity(0.2)
: null,
border: Border.all(
color: theme.highlightColor,
Expand All @@ -88,7 +88,9 @@ class SudokuBlock extends StatelessWidget {
? theme.colorScheme.error
: block.isGenerated
? null
: SudokuColors.darkPurple,
: theme.brightness == Brightness.light
? SudokuColors.darkPurple
: SudokuColors.lightPurple.withGreen(224),
),
),
),
Expand Down
Loading

0 comments on commit 4d543ed

Please sign in to comment.