Implemented popup & confetti

This commit is contained in:
2025-07-20 22:36:55 +02:00
parent ddc2d68e9b
commit 5099dafbe9
2 changed files with 276 additions and 206 deletions

View File

@@ -8,6 +8,7 @@ import 'package:cabo_counter/presentation/views/mode_selection_view.dart';
import 'package:cabo_counter/presentation/views/points_view.dart';
import 'package:cabo_counter/presentation/views/round_view.dart';
import 'package:cabo_counter/services/local_storage_service.dart';
import 'package:confetti/confetti.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -21,6 +22,9 @@ class ActiveGameView extends StatefulWidget {
}
class _ActiveGameViewState extends State<ActiveGameView> {
final confettiController = ConfettiController(
duration: const Duration(seconds: 10),
);
late final GameSession gameSession;
late List<int> denseRanks;
late List<int> sortedPlayerIndices;
@@ -33,7 +37,9 @@ class _ActiveGameViewState extends State<ActiveGameView> {
@override
Widget build(BuildContext context) {
return ListenableBuilder(
return Stack(
children: [
ListenableBuilder(
listenable: gameSession,
builder: (context, _) {
sortedPlayerIndices = _getSortedPlayerIndices();
@@ -76,7 +82,8 @@ class _ActiveGameViewState extends State<ActiveGameView> {
trailing: Row(
children: [
const SizedBox(width: 5),
Text('${gameSession.playerScores[playerIndex]} '
Text(
'${gameSession.playerScores[playerIndex]} '
'${AppLocalizations.of(context).points}')
],
),
@@ -103,15 +110,15 @@ class _ActiveGameViewState extends State<ActiveGameView> {
title: Text(
'${AppLocalizations.of(context).round} ${index + 1}',
),
trailing:
index + 1 != gameSession.roundNumber ||
trailing: index + 1 !=
gameSession.roundNumber ||
gameSession.isGameFinished == true
? (const Text('\u{2705}',
style: TextStyle(fontSize: 22)))
: const Text('\u{23F3}',
style: TextStyle(fontSize: 22)),
onTap: () async {
_openRoundView(index + 1);
_openRoundView(context, index + 1);
},
));
},
@@ -127,7 +134,8 @@ class _ActiveGameViewState extends State<ActiveGameView> {
children: [
CupertinoListTile(
title: Text(
AppLocalizations.of(context).scoring_history,
AppLocalizations.of(context)
.scoring_history,
),
backgroundColorActivated:
CustomTheme.backgroundColor,
@@ -168,7 +176,8 @@ class _ActiveGameViewState extends State<ActiveGameView> {
style: gameSession.roundNumber > 1 &&
!gameSession.isGameFinished
? const TextStyle(color: Colors.white)
: const TextStyle(color: Colors.white30),
: const TextStyle(
color: Colors.white30),
),
backgroundColorActivated:
CustomTheme.backgroundColor,
@@ -205,7 +214,8 @@ class _ActiveGameViewState extends State<ActiveGameView> {
context,
CupertinoPageRoute(
builder: (_) => CreateGameView(
gameTitle: gameSession.gameTitle,
gameTitle:
gameSession.gameTitle,
gameMode: widget.gameSession
.isPointsLimitEnabled ==
true
@@ -228,15 +238,19 @@ class _ActiveGameViewState extends State<ActiveGameView> {
if (!success && context.mounted) {
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: Text(AppLocalizations.of(context)
builder: (context) =>
CupertinoAlertDialog(
title: Text(
AppLocalizations.of(context)
.export_error_title),
content: Text(AppLocalizations.of(context)
content: Text(
AppLocalizations.of(context)
.export_error_message),
actions: [
CupertinoDialogAction(
child: Text(
AppLocalizations.of(context).ok),
AppLocalizations.of(context)
.ok),
onPressed: () =>
Navigator.pop(context),
),
@@ -245,13 +259,36 @@ class _ActiveGameViewState extends State<ActiveGameView> {
);
}
}),
CupertinoListTile(
title: const Text('Konfetti'),
onTap: () => confettiController.play(),
)
],
)
],
),
),
));
});
}),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: ConfettiWidget(
blastDirectionality: BlastDirectionality.explosive,
particleDrag: 0.07,
emissionFrequency: 0.1,
numberOfParticles: 10,
minBlastForce: 5,
maxBlastForce: 20,
confettiController: confettiController,
),
),
],
),
],
);
}
/// Shows a dialog to confirm ending the game.
@@ -403,7 +440,7 @@ class _ActiveGameViewState extends State<ActiveGameView> {
/// Recursively opens the RoundView for the specified round number.
/// It starts with the given [roundNumber] and continues to open the next round
/// until the user navigates back or the round number is invalid.
void _openRoundView(int roundNumber) async {
void _openRoundView(BuildContext context, int roundNumber) async {
final val = await Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
fullscreenDialog: true,
@@ -413,10 +450,42 @@ class _ActiveGameViewState extends State<ActiveGameView> {
),
),
);
if (widget.gameSession.isGameFinished && mounted) {
String winner = widget.gameSession.winner;
int winnerIndex = widget.gameSession.players.indexOf(winner);
int points = widget.gameSession.playerScores[winnerIndex];
confettiController.play();
await Future.delayed(const Duration(milliseconds: 300));
if (context.mounted) {
showCupertinoDialog(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text(AppLocalizations.of(context).end_of_game_title),
content: Text(AppLocalizations.of(context)
.end_of_game_message(1, winner, points)),
actions: [
CupertinoDialogAction(
child: Text(AppLocalizations.of(context).ok),
onPressed: () {
confettiController.stop();
Navigator.pop(context);
},
),
],
);
});
}
}
if (val != null && val >= 0) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 600));
_openRoundView(val);
if (context.mounted) {
_openRoundView(context, val);
}
});
}
}

View File

@@ -2,7 +2,7 @@ name: cabo_counter
description: "Mobile app for the card game Cabo"
publish_to: 'none'
version: 0.5.1+568
version: 0.5.2+579
environment:
sdk: ^3.5.4
@@ -30,6 +30,7 @@ dependencies:
rate_my_app: ^2.3.2
reorderables: ^0.4.2
collection: ^1.18.0
confetti: ^0.6.0
dev_dependencies:
flutter_test: