From 373c0bf93fd9a183c121c30e8de509dd73c9cff5 Mon Sep 17 00:00:00 2001 From: FMorschel <52160996+FMorschel@users.noreply.github.com> Date: Thu, 4 Sep 2025 23:42:00 -0300 Subject: [PATCH 1/2] Implement countdown timer and game over dialog in DashSweeper --- lib/dash_sweeper.dart | 89 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/lib/dash_sweeper.dart b/lib/dash_sweeper.dart index 8cbce5e..e209dea 100644 --- a/lib/dash_sweeper.dart +++ b/lib/dash_sweeper.dart @@ -14,15 +14,36 @@ class DashSweeper extends StatefulWidget { State createState() => _DashSweeperState(); } -class _DashSweeperState extends State { +class _DashSweeperState extends State + with TickerProviderStateMixin { + static const countdownMax = 3; + late List tiles; + late Animation countdown; + late AnimationController countdownController; + + bool paused = false; @override void initState() { super.initState(); + countdownController = AnimationController( + vsync: this, + duration: Duration(seconds: countdownMax), + ); + countdown = Tween( + begin: countdownMax.toDouble(), + end: 0, + ).animate(countdownController); _generateTiles(widget.rows, widget.columns); } + @override + void dispose() { + countdownController.dispose(); + super.dispose(); + } + void _generateTiles(int rows, int columns) { // calculate tiles final amount = rows * columns; @@ -200,6 +221,10 @@ class _DashSweeperState extends State { .map((tile) => tile.copyWith(state: TileState.revealed)) .toList(growable: false); }); + _gameOverDialog(); + } + + void _gameOverDialog() { showDialog( context: context, barrierDismissible: false, @@ -208,8 +233,8 @@ class _DashSweeperState extends State { title: Text('Game Over'), content: Text('Bummer...'), actions: [ - OutlinedButton( - onPressed: () {}, + ElevatedButton( + onPressed: _timerToDialog, child: Text('Show field'), ), ElevatedButton( @@ -225,6 +250,17 @@ class _DashSweeperState extends State { ); } + void _timerToDialog() { + Navigator.of(context).pop(); + countdownController.forward(from: 0); + countdownController.addStatusListener((status) { + if (status == AnimationStatus.completed) { + if (mounted) _gameOverDialog(); + countdownController.reset(); + } + }); + } + /// Recursive function to get all empty tiles to reveal at once. Set emptyTilesToReveal( int index, { @@ -252,6 +288,53 @@ class _DashSweeperState extends State { @override Widget build(BuildContext context) { return Scaffold( + appBar: AppBar( + title: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text('DashSweeper'), + AnimatedBuilder( + animation: countdown, + builder: (context, child) { + if (countdownController.value == 0) return SizedBox.shrink(); + return Padding( + padding: const EdgeInsets.only(left: 8.0), + child: Stack( + alignment: Alignment.center, + children: [ + CircularProgressIndicator.adaptive( + value: countdown.value / countdownMax, + ), + IconButton( + onPressed: () { + setState(() { + paused = !paused; + }); + if (paused) { + countdownController.stop(); + } else { + countdownController.forward(); + } + }, + icon: Icon( + paused ? Icons.play_arrow : Icons.pause, + ), + ), + ], + ), + ); + }, + ), + ], + ), + centerTitle: true, + actions: [ + IconButton( + onPressed: _resetGame, + icon: Icon(Icons.refresh), + ), + ], + ), body: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: widget.columns, From 1b289736fcd5a6b583f3bdcca9f7e698fa0316f0 Mon Sep 17 00:00:00 2001 From: FMorschel <52160996+FMorschel@users.noreply.github.com> Date: Thu, 4 Sep 2025 23:48:40 -0300 Subject: [PATCH 2/2] fixes edge cases --- lib/dash_sweeper.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/dash_sweeper.dart b/lib/dash_sweeper.dart index e209dea..0af1a9b 100644 --- a/lib/dash_sweeper.dart +++ b/lib/dash_sweeper.dart @@ -210,6 +210,8 @@ class _DashSweeperState extends State } void _resetGame() { + paused = false; + countdownController.reset(); setState(() { _generateTiles(widget.rows, widget.columns); }); @@ -255,8 +257,10 @@ class _DashSweeperState extends State countdownController.forward(from: 0); countdownController.addStatusListener((status) { if (status == AnimationStatus.completed) { - if (mounted) _gameOverDialog(); - countdownController.reset(); + if (mounted && (ModalRoute.of(context)?.isCurrent ?? false)) { + _gameOverDialog(); + countdownController.reset(); + } } }); }