From ddc2d68e9b6c79fcb045e1a4b778728bee8e4b1a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 22:36:48 +0200 Subject: [PATCH 01/15] Added Strings for popup --- lib/l10n/arb/app_de.arb | 15 +++++++++++++++ lib/l10n/arb/app_en.arb | 15 +++++++++++++++ lib/l10n/generated/app_localizations.dart | 12 ++++++++++++ lib/l10n/generated/app_localizations_de.dart | 15 +++++++++++++++ lib/l10n/generated/app_localizations_en.dart | 14 ++++++++++++++ 5 files changed, 71 insertions(+) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index effbd96..c8dad5c 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -96,6 +96,21 @@ } }, + "end_of_game_title": "Spiel beendet", + "end_of_game_message": "{playerCount, plural, =1{{names} hat das Spiel mit {points} Punkten gewonnen. Glückwunsch!} other{{names} haben das Spiel mit {points} Punkten gewonnen. Glückwunsch!}}", + "@end_of_game_message": { + "placeholders": { + "playerCount": { + "type": "int" + }, + "names": { + "type": "String" + }, + "points": { + "type": "int" + } + } + }, "end_game": "Spiel beenden", "delete_game": "Spiel löschen", "new_game_same_settings": "Neues Spiel mit gleichen Einstellungen", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index ea169e9..ea4ab8d 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -96,6 +96,21 @@ } }, + "end_of_game_title": "End of Game", + "end_of_game_message": "{playerCount, plural, =1{{names} won the game with {points} points. Congratulations!} other{{names} won the game with {points} points. Congratulations!}}", + "@game_over_message": { + "placeholders": { + "playerCount": { + "type": "int" + }, + "names": { + "type": "String" + }, + "points": { + "type": "int" + } + } + }, "end_game": "End Game", "delete_game": "Delete Game", "new_game_same_settings": "New Game with same Settings", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 9c64cb2..5749079 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -453,6 +453,18 @@ abstract class AppLocalizations { String bonus_points_message( int playerCount, String names, int pointLimit, int bonusPoints); + /// No description provided for @end_of_game_title. + /// + /// In de, this message translates to: + /// **'Spiel beendet'** + String get end_of_game_title; + + /// No description provided for @end_of_game_message. + /// + /// In de, this message translates to: + /// **'{playerCount, plural, =1{{names} hat das Spiel mit {points} Punkten gewonnen. Glückwunsch!} other{{names} haben das Spiel mit {points} Punkten gewonnen. Glückwunsch!}}'** + String end_of_game_message(int playerCount, String names, int points); + /// No description provided for @end_game. /// /// In de, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index c4849d8..87745dd 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -208,6 +208,21 @@ class AppLocalizationsDe extends AppLocalizations { return '$_temp0'; } + @override + String get end_of_game_title => 'Spiel beendet'; + + @override + String end_of_game_message(int playerCount, String names, int points) { + String _temp0 = intl.Intl.pluralLogic( + playerCount, + locale: localeName, + other: + '$names haben das Spiel mit $points Punkten gewonnen. Glückwunsch!', + one: '$names hat das Spiel mit $points Punkten gewonnen. Glückwunsch!', + ); + return '$_temp0'; + } + @override String get end_game => 'Spiel beenden'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 6b2689e..2da405c 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -205,6 +205,20 @@ class AppLocalizationsEn extends AppLocalizations { return '$_temp0'; } + @override + String get end_of_game_title => 'End of Game'; + + @override + String end_of_game_message(int playerCount, String names, int points) { + String _temp0 = intl.Intl.pluralLogic( + playerCount, + locale: localeName, + other: '$names won the game with $points points. Congratulations!', + one: '$names won the game with $points points. Congratulations!', + ); + return '$_temp0'; + } + @override String get end_game => 'End Game'; From 5099dafbe90ca4ab149bc1e14c29abf5b37dcfec Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 22:36:55 +0200 Subject: [PATCH 02/15] Implemented popup & confetti --- lib/presentation/views/active_game_view.dart | 479 +++++++++++-------- pubspec.yaml | 3 +- 2 files changed, 276 insertions(+), 206 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index dece0a6..a14194b 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -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 { + final confettiController = ConfettiController( + duration: const Duration(seconds: 10), + ); late final GameSession gameSession; late List denseRanks; late List sortedPlayerIndices; @@ -33,225 +37,258 @@ class _ActiveGameViewState extends State { @override Widget build(BuildContext context) { - return ListenableBuilder( - listenable: gameSession, - builder: (context, _) { - sortedPlayerIndices = _getSortedPlayerIndices(); - denseRanks = _calculateDenseRank( - gameSession.playerScores, sortedPlayerIndices); - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text(gameSession.gameTitle), - ), - child: SafeArea( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).players, - style: CustomTheme.rowTitle, - ), - ), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: gameSession.players.length, - itemBuilder: (BuildContext context, int index) { - int playerIndex = sortedPlayerIndices[index]; - return CupertinoListTile( - title: Row( - children: [ - _getPlacementTextWidget(index), - const SizedBox(width: 5), - Text( - gameSession.players[playerIndex], - style: const TextStyle( - fontWeight: FontWeight.bold), - ), - ], + return Stack( + children: [ + ListenableBuilder( + listenable: gameSession, + builder: (context, _) { + sortedPlayerIndices = _getSortedPlayerIndices(); + denseRanks = _calculateDenseRank( + gameSession.playerScores, sortedPlayerIndices); + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text(gameSession.gameTitle), + ), + child: SafeArea( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).players, + style: CustomTheme.rowTitle, ), - trailing: Row( - children: [ - const SizedBox(width: 5), - Text('${gameSession.playerScores[playerIndex]} ' - '${AppLocalizations.of(context).points}') - ], - ), - ); - }, - ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).rounds, - style: CustomTheme.rowTitle, - ), - ), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: gameSession.roundNumber, - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: const EdgeInsets.all(1), - child: CupertinoListTile( - backgroundColorActivated: - CustomTheme.backgroundColor, - title: Text( - '${AppLocalizations.of(context).round} ${index + 1}', + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: gameSession.players.length, + itemBuilder: (BuildContext context, int index) { + int playerIndex = sortedPlayerIndices[index]; + return CupertinoListTile( + title: Row( + children: [ + _getPlacementTextWidget(index), + const SizedBox(width: 5), + Text( + gameSession.players[playerIndex], + style: const TextStyle( + fontWeight: FontWeight.bold), + ), + ], ), - trailing: - index + 1 != gameSession.roundNumber || + trailing: Row( + children: [ + const SizedBox(width: 5), + Text( + '${gameSession.playerScores[playerIndex]} ' + '${AppLocalizations.of(context).points}') + ], + ), + ); + }, + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).rounds, + style: CustomTheme.rowTitle, + ), + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: gameSession.roundNumber, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(1), + child: CupertinoListTile( + backgroundColorActivated: + CustomTheme.backgroundColor, + title: Text( + '${AppLocalizations.of(context).round} ${index + 1}', + ), + 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); - }, - )); - }, - ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).statistics, - style: CustomTheme.rowTitle, - ), - ), - Column( - children: [ - CupertinoListTile( - title: Text( - AppLocalizations.of(context).scoring_history, + onTap: () async { + _openRoundView(context, index + 1); + }, + )); + }, + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).statistics, + style: CustomTheme.rowTitle, + ), + ), + Column( + children: [ + CupertinoListTile( + title: Text( + AppLocalizations.of(context) + .scoring_history, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => GraphView( + gameSession: gameSession, + )))), + CupertinoListTile( + title: Text( + AppLocalizations.of(context).point_overview, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => PointsView( + gameSession: gameSession, + )))), + ], + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).game, + style: CustomTheme.rowTitle, + ), + ), + Column( + children: [ + Visibility( + visible: !gameSession.isPointsLimitEnabled, + child: CupertinoListTile( + title: Text( + AppLocalizations.of(context).end_game, + style: gameSession.roundNumber > 1 && + !gameSession.isGameFinished + ? const TextStyle(color: Colors.white) + : const TextStyle( + color: Colors.white30), + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () { + if (gameSession.roundNumber > 1 && + !gameSession.isGameFinished) { + _showEndGameDialog(); + } + }), ), - backgroundColorActivated: - CustomTheme.backgroundColor, - onTap: () => Navigator.push( - context, - CupertinoPageRoute( - builder: (_) => GraphView( - gameSession: gameSession, - )))), - CupertinoListTile( - title: Text( - AppLocalizations.of(context).point_overview, - ), - backgroundColorActivated: - CustomTheme.backgroundColor, - onTap: () => Navigator.push( - context, - CupertinoPageRoute( - builder: (_) => PointsView( - gameSession: gameSession, - )))), - ], - ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).game, - style: CustomTheme.rowTitle, - ), - ), - Column( - children: [ - Visibility( - visible: !gameSession.isPointsLimitEnabled, - child: CupertinoListTile( + CupertinoListTile( title: Text( - AppLocalizations.of(context).end_game, - style: gameSession.roundNumber > 1 && - !gameSession.isGameFinished - ? const TextStyle(color: Colors.white) - : const TextStyle(color: Colors.white30), + AppLocalizations.of(context).delete_game, ), backgroundColorActivated: CustomTheme.backgroundColor, onTap: () { - if (gameSession.roundNumber > 1 && - !gameSession.isGameFinished) { - _showEndGameDialog(); - } - }), - ), - CupertinoListTile( - title: Text( - AppLocalizations.of(context).delete_game, - ), - backgroundColorActivated: - CustomTheme.backgroundColor, - onTap: () { - _showDeleteGameDialog().then((value) { - if (value) { - _removeGameSession(gameSession); - } - }); - }, - ), - CupertinoListTile( - title: Text( - AppLocalizations.of(context) - .new_game_same_settings, - ), - backgroundColorActivated: - CustomTheme.backgroundColor, - onTap: () { - Navigator.pushReplacement( - context, - CupertinoPageRoute( - builder: (_) => CreateGameView( - gameTitle: gameSession.gameTitle, - gameMode: widget.gameSession - .isPointsLimitEnabled == - true - ? GameMode.pointLimit - : GameMode.unlimited, - players: gameSession.players, - ))); - }, - ), - CupertinoListTile( - title: Text( - AppLocalizations.of(context).export_game, + _showDeleteGameDialog().then((value) { + if (value) { + _removeGameSession(gameSession); + } + }); + }, ), - backgroundColorActivated: - CustomTheme.backgroundColor, - onTap: () async { - final success = await LocalStorageService - .exportSingleGameSession( - widget.gameSession); - if (!success && context.mounted) { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text(AppLocalizations.of(context) - .export_error_title), - content: Text(AppLocalizations.of(context) - .export_error_message), - actions: [ - CupertinoDialogAction( - child: Text( - AppLocalizations.of(context).ok), - onPressed: () => - Navigator.pop(context), + CupertinoListTile( + title: Text( + AppLocalizations.of(context) + .new_game_same_settings, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () { + Navigator.pushReplacement( + context, + CupertinoPageRoute( + builder: (_) => CreateGameView( + gameTitle: + gameSession.gameTitle, + gameMode: widget.gameSession + .isPointsLimitEnabled == + true + ? GameMode.pointLimit + : GameMode.unlimited, + players: gameSession.players, + ))); + }, + ), + CupertinoListTile( + title: Text( + AppLocalizations.of(context).export_game, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () async { + final success = await LocalStorageService + .exportSingleGameSession( + widget.gameSession); + if (!success && context.mounted) { + showCupertinoDialog( + context: context, + builder: (context) => + CupertinoAlertDialog( + title: Text( + AppLocalizations.of(context) + .export_error_title), + content: Text( + AppLocalizations.of(context) + .export_error_message), + actions: [ + CupertinoDialogAction( + child: Text( + AppLocalizations.of(context) + .ok), + onPressed: () => + Navigator.pop(context), + ), + ], ), - ], - ), - ); - } - }), + ); + } + }), + 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 { /// 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 { ), ), ); + 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); + } }); } } diff --git a/pubspec.yaml b/pubspec.yaml index c9be09f..06a051a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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: From 4a8abbbe7276d9f8dbb49a326d118fca6b9869f7 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 22:49:20 +0200 Subject: [PATCH 03/15] Extracted code to method _playFinishAnimation() --- lib/presentation/views/active_game_view.dart | 70 +++++++++++--------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index a14194b..71fd949 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -441,7 +441,7 @@ class _ActiveGameViewState extends State { /// 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(BuildContext context, int roundNumber) async { - final val = await Navigator.of(context, rootNavigator: true).push( + final round = await Navigator.of(context, rootNavigator: true).push( CupertinoPageRoute( fullscreenDialog: true, builder: (context) => RoundView( @@ -450,43 +450,51 @@ class _ActiveGameViewState extends State { ), ), ); - 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 (widget.gameSession.isGameFinished && context.mounted) { + _playFinishAnimation(context); } - if (val != null && val >= 0) { + + // If the previous round was not the last one + if (round != null && round >= 0) { WidgetsBinding.instance.addPostFrameCallback((_) async { await Future.delayed(const Duration(milliseconds: 600)); if (context.mounted) { - _openRoundView(context, val); + _openRoundView(context, round); } }); } } + + /// Plays the confetti animation and shows a dialog with the winner's information. + Future _playFinishAnimation(BuildContext context) async { + 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); + }, + ), + ], + ); + }); + } + } } From 864c21b77c54ebbc15a97b767f6edd62e04eacbe Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 22:49:35 +0200 Subject: [PATCH 04/15] Replaced tenary operator with Visibility Widget --- lib/presentation/views/graph_view.dart | 100 +++++++++++++------------ 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 23137cd..4147f51 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -25,60 +25,64 @@ class _GraphViewState extends State { @override Widget build(BuildContext context) { + print('roundNumber: ${widget.gameSession.roundNumber}'); return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text(AppLocalizations.of(context).scoring_history), previousPageTitle: AppLocalizations.of(context).back, ), - child: widget.gameSession.roundNumber > 1 - ? Padding( - padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), - child: SfCartesianChart( - enableAxisAnimation: true, - legend: const Legend( - overflowMode: LegendItemOverflowMode.wrap, - isVisible: true, - position: LegendPosition.bottom), - primaryXAxis: const NumericAxis( - labelStyle: TextStyle(fontWeight: FontWeight.bold), - interval: 1, - decimalPlaces: 0, - ), - primaryYAxis: NumericAxis( - labelStyle: const TextStyle(fontWeight: FontWeight.bold), - labelAlignment: LabelAlignment.center, - labelPosition: ChartDataLabelPosition.inside, - interval: 1, - decimalPlaces: 0, - axisLabelFormatter: (AxisLabelRenderDetails details) { - if (details.value == 0) { - return ChartAxisLabel('', const TextStyle()); - } - return ChartAxisLabel( - '${details.value.toInt()}', const TextStyle()); - }, - ), - series: getCumulativeScores(), + child: Visibility( + visible: widget.gameSession.roundNumber > 1 || + widget.gameSession.isGameFinished, + replacement: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Center( + child: Icon(CupertinoIcons.chart_bar_alt_fill, size: 60), + ), + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Text( + AppLocalizations.of(context).empty_graph_text, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), ), - ) - : Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Center( - child: Icon(CupertinoIcons.chart_bar_alt_fill, size: 60), - ), - const SizedBox(height: 10), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 40), - child: Text( - AppLocalizations.of(context).empty_graph_text, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 16), - ), - ), - ], - )); + ), + ], + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), + child: SfCartesianChart( + enableAxisAnimation: true, + legend: const Legend( + overflowMode: LegendItemOverflowMode.wrap, + isVisible: true, + position: LegendPosition.bottom), + primaryXAxis: const NumericAxis( + labelStyle: TextStyle(fontWeight: FontWeight.bold), + interval: 1, + decimalPlaces: 0, + ), + primaryYAxis: NumericAxis( + labelStyle: const TextStyle(fontWeight: FontWeight.bold), + labelAlignment: LabelAlignment.center, + labelPosition: ChartDataLabelPosition.inside, + interval: 1, + decimalPlaces: 0, + axisLabelFormatter: (AxisLabelRenderDetails details) { + if (details.value == 0) { + return ChartAxisLabel('', const TextStyle()); + } + return ChartAxisLabel( + '${details.value.toInt()}', const TextStyle()); + }, + ), + series: getCumulativeScores(), + ), + ), + )); } /// Returns a list of LineSeries representing the cumulative scores of each player. From 357f74d4aa0ccdf186a0eaf0564ae587727e19fe Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 22:49:48 +0200 Subject: [PATCH 05/15] Replaced tenary operator with Visibility Widget --- lib/presentation/views/main_menu_view.dart | 286 ++++++++++----------- pubspec.yaml | 2 +- 2 files changed, 142 insertions(+), 146 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index f8817a0..dd01e03 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -58,160 +58,156 @@ class _MainMenuViewState extends State { listenable: gameManager, builder: (context, _) { return CupertinoPageScaffold( - resizeToAvoidBottomInset: false, - navigationBar: CupertinoNavigationBar( - leading: IconButton( - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => const SettingsView(), - ), - ).then((_) { - setState(() {}); - }); - }, - icon: const Icon(CupertinoIcons.settings, size: 30)), - middle: Text(AppLocalizations.of(context).app_name), - trailing: IconButton( - onPressed: () => Navigator.push( + resizeToAvoidBottomInset: false, + navigationBar: CupertinoNavigationBar( + leading: IconButton( + onPressed: () { + Navigator.push( context, CupertinoPageRoute( - builder: (context) => CreateGameView( - gameMode: ConfigService.getGameMode()), + builder: (context) => const SettingsView(), ), + ).then((_) { + setState(() {}); + }); + }, + icon: const Icon(CupertinoIcons.settings, size: 30)), + middle: Text(AppLocalizations.of(context).app_name), + trailing: IconButton( + onPressed: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => CreateGameView( + gameMode: ConfigService.getGameMode()), + ), + ), + icon: const Icon(CupertinoIcons.add)), + ), + child: CupertinoPageScaffold( + child: SafeArea( + child: Visibility( + replacement: + const Center(child: CupertinoActivityIndicator()), + child: Visibility( + visible: gameManager.gameList.isEmpty, + replacement: ListView.separated( + itemCount: gameManager.gameList.length, + separatorBuilder: (context, index) => Divider( + height: 1, + thickness: 0.5, + color: CustomTheme.white.withAlpha(50), + indent: 50, + endIndent: 50, ), - icon: const Icon(CupertinoIcons.add)), - ), - child: CupertinoPageScaffold( - child: SafeArea( - child: _isLoading - ? const Center(child: CupertinoActivityIndicator()) - : gameManager.gameList.isEmpty - ? Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 30), - Center( - child: GestureDetector( - onTap: () => Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => CreateGameView( - gameMode: ConfigService.getGameMode()), + itemBuilder: (context, index) { + final session = gameManager.gameList[index]; + return ListenableBuilder( + listenable: session, + builder: (context, _) { + return Dismissible( + key: Key(session.id), + background: Container( + color: CupertinoColors.destructiveRed, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 20.0), + child: const Icon( + CupertinoIcons.delete, + color: CupertinoColors.white, ), ), - child: Icon( - CupertinoIcons.plus, - size: 60, - color: CustomTheme.primaryColor, + direction: DismissDirection.endToStart, + confirmDismiss: (direction) async { + return await _showDeleteGamePopup( + context, session.gameTitle); + }, + onDismissed: (direction) { + gameManager.removeGameSessionById(session.id); + }, + dismissThresholds: const { + DismissDirection.startToEnd: 0.6 + }, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 10.0), + child: CupertinoListTile( + backgroundColorActivated: + CustomTheme.backgroundColor, + title: Text(session.gameTitle), + subtitle: Visibility( + visible: session.isGameFinished, + replacement: Text( + '${AppLocalizations.of(context).mode}: ${_translateGameMode(session.isPointsLimitEnabled)}', + style: const TextStyle(fontSize: 14), + ), + child: Text( + '\u{1F947} ${session.winner}', + style: const TextStyle(fontSize: 14), + )), + trailing: Row( + children: [ + Text('${session.roundNumber}'), + const SizedBox(width: 3), + const Icon(CupertinoIcons + .arrow_2_circlepath_circle_fill), + const SizedBox(width: 15), + Text('${session.players.length}'), + const SizedBox(width: 3), + const Icon( + CupertinoIcons.person_2_fill), + ], + ), + onTap: () { + final session = + gameManager.gameList[index]; + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ActiveGameView( + gameSession: session), + ), + ).then((_) { + setState(() {}); + }); + }, + ), ), - )), - const SizedBox(height: 10), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 70), - child: Text( - '${AppLocalizations.of(context).empty_text_1}\n${AppLocalizations.of(context).empty_text_2}', - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 16), - ), - ), - ], - ) - : ListView.separated( - itemCount: gameManager.gameList.length, - separatorBuilder: (context, index) => Divider( - height: 1, - thickness: 0.5, - color: CustomTheme.white.withAlpha(50), - indent: 50, - endIndent: 50, + ); + }); + }, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 30), + Center( + child: GestureDetector( + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => CreateGameView( + gameMode: ConfigService.getGameMode()), ), - itemBuilder: (context, index) { - final session = gameManager.gameList[index]; - return ListenableBuilder( - listenable: session, - builder: (context, _) { - return Dismissible( - key: Key(session.id), - background: Container( - color: CupertinoColors.destructiveRed, - alignment: Alignment.centerRight, - padding: - const EdgeInsets.only(right: 20.0), - child: const Icon( - CupertinoIcons.delete, - color: CupertinoColors.white, - ), - ), - direction: DismissDirection.endToStart, - confirmDismiss: (direction) async { - return await _showDeleteGamePopup( - context, session.gameTitle); - }, - onDismissed: (direction) { - gameManager - .removeGameSessionById(session.id); - }, - dismissThresholds: const { - DismissDirection.startToEnd: 0.6 - }, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 10.0), - child: CupertinoListTile( - backgroundColorActivated: - CustomTheme.backgroundColor, - title: Text(session.gameTitle), - subtitle: - session.isGameFinished == true - ? Text( - '\u{1F947} ${session.winner}', - style: const TextStyle( - fontSize: 14), - ) - : Text( - '${AppLocalizations.of(context).mode}: ${_translateGameMode(session.isPointsLimitEnabled)}', - style: const TextStyle( - fontSize: 14), - ), - trailing: Row( - children: [ - Text('${session.roundNumber}'), - const SizedBox(width: 3), - const Icon(CupertinoIcons - .arrow_2_circlepath_circle_fill), - const SizedBox(width: 15), - Text('${session.players.length}'), - const SizedBox(width: 3), - const Icon( - CupertinoIcons.person_2_fill), - ], - ), - onTap: () { - final session = - gameManager.gameList[index]; - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => - ActiveGameView( - gameSession: session), - ), - ).then((_) { - setState(() {}); - }); - }, - ), - ), - ); - }); - }, ), - ), - ), - ); + child: Icon( + CupertinoIcons.plus, + size: 60, + color: CustomTheme.primaryColor, + ), + )), + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 70), + child: Text( + '${AppLocalizations.of(context).empty_text_1}\n${AppLocalizations.of(context).empty_text_2}', + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + ), + ))); }); } diff --git a/pubspec.yaml b/pubspec.yaml index 06a051a..0bc543c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.5.2+579 +version: 0.5.2+580 environment: sdk: ^3.5.4 From 382fab57dd6228fad183d243a78274166231bcd4 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 22:51:11 +0200 Subject: [PATCH 06/15] Used variable again --- lib/presentation/views/main_menu_view.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index dd01e03..2f2044a 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -86,9 +86,8 @@ class _MainMenuViewState extends State { child: CupertinoPageScaffold( child: SafeArea( child: Visibility( - replacement: - const Center(child: CupertinoActivityIndicator()), - child: Visibility( + visible: _isLoading, + replacement: Visibility( visible: gameManager.gameList.isEmpty, replacement: ListView.separated( itemCount: gameManager.gameList.length, @@ -206,6 +205,7 @@ class _MainMenuViewState extends State { ], ), ), + child: const Center(child: CupertinoActivityIndicator()), ), ))); }); From 2a7b2a9dc0dc48b754d655e20f833b96e66c83e8 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 22:56:41 +0200 Subject: [PATCH 07/15] Added delays in constants.dart --- lib/core/constants.dart | 9 +++++++++ lib/presentation/views/active_game_view.dart | 3 ++- lib/presentation/views/create_game_view.dart | 4 +++- lib/presentation/views/main_menu_view.dart | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/core/constants.dart b/lib/core/constants.dart index e716464..e1c2f8d 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -19,4 +19,13 @@ class Constants { remindDays: 45, minLaunches: 15, remindLaunches: 40); + + /// Delay in milliseconds before a pop-up appears. + static const int popUpDelay = 300; + + /// Delay in milliseconds before the round view appears after the previous one is closed. + static const int roundViewDelay = 600; + + /// Duration in milliseconds for the fade-in animation of texts. + static const int fadeInDuration = 300; } diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 71fd949..58f5765 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; @@ -474,7 +475,7 @@ class _ActiveGameViewState extends State { confettiController.play(); - await Future.delayed(const Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: Constants.popUpDelay)); if (context.mounted) { showCupertinoDialog( diff --git a/lib/presentation/views/create_game_view.dart b/lib/presentation/views/create_game_view.dart index 870ea29..236baff 100644 --- a/lib/presentation/views/create_game_view.dart +++ b/lib/presentation/views/create_game_view.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; @@ -187,7 +188,8 @@ class _CreateGameViewState extends State { opacity: _playerNameTextControllers.length > 1 ? 1.0 : 0.0, - duration: const Duration(milliseconds: 300), + duration: const Duration( + milliseconds: Constants.fadeInDuration), child: Padding( padding: const EdgeInsets.only(right: 8.0), child: ReorderableDragStartListener( diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 2f2044a..6d8262b 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -239,7 +239,7 @@ class _MainMenuViewState extends State { BadRatingDialogDecision badRatingDecision = BadRatingDialogDecision.cancel; // so that the bad rating dialog is not shown immediately - await Future.delayed(const Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: Constants.popUpDelay)); switch (preRatingDecision) { case PreRatingDialogDecision.yes: From 43fd53a011ff964dffd6c81686b7407778c2ca6b Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 23:12:19 +0200 Subject: [PATCH 08/15] Removed confetti button --- lib/presentation/views/active_game_view.dart | 4 ---- pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 58f5765..1c10095 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -260,10 +260,6 @@ class _ActiveGameViewState extends State { ); } }), - CupertinoListTile( - title: const Text('Konfetti'), - onTap: () => confettiController.play(), - ) ], ) ], diff --git a/pubspec.yaml b/pubspec.yaml index 0bc543c..01d641d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.5.2+580 +version: 0.5.2+581 environment: sdk: ^3.5.4 From b2100412093861d3d5a6e80b6e39186d24cad089 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 23:16:25 +0200 Subject: [PATCH 09/15] Updated strings --- lib/l10n/arb/app_de.arb | 24 ++++++++++++------------ lib/l10n/arb/app_en.arb | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index c8dad5c..cd1a8c7 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -98,18 +98,18 @@ "end_of_game_title": "Spiel beendet", "end_of_game_message": "{playerCount, plural, =1{{names} hat das Spiel mit {points} Punkten gewonnen. Glückwunsch!} other{{names} haben das Spiel mit {points} Punkten gewonnen. Glückwunsch!}}", - "@end_of_game_message": { - "placeholders": { - "playerCount": { - "type": "int" - }, - "names": { - "type": "String" - }, - "points": { - "type": "int" - } - } + "@end_of_game_message": { + "placeholders": { + "playerCount": { + "type": "int" + }, + "names": { + "type": "String" + }, + "points": { + "type": "int" + } + } }, "end_game": "Spiel beenden", "delete_game": "Spiel löschen", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index ea4ab8d..306768f 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -98,7 +98,7 @@ "end_of_game_title": "End of Game", "end_of_game_message": "{playerCount, plural, =1{{names} won the game with {points} points. Congratulations!} other{{names} won the game with {points} points. Congratulations!}}", - "@game_over_message": { + "@end_of_game_message": { "placeholders": { "playerCount": { "type": "int" From 7988e3764a25de7487d4d727b3ee98448932d919 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 23:16:30 +0200 Subject: [PATCH 10/15] Removed print --- lib/presentation/views/graph_view.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 4147f51..da9f8e7 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -25,7 +25,6 @@ class _GraphViewState extends State { @override Widget build(BuildContext context) { - print('roundNumber: ${widget.gameSession.roundNumber}'); return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text(AppLocalizations.of(context).scoring_history), From dc19bf6f4cd42037f901409e19e930da97e15f53 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 23:16:58 +0200 Subject: [PATCH 11/15] Added dispose for confettiController --- lib/presentation/views/active_game_view.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 1c10095..c3510a2 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -494,4 +494,10 @@ class _ActiveGameViewState extends State { }); } } + + @override + void dispose() { + confettiController.dispose(); + super.dispose(); + } } From b38a3be28860875443dca44e30df5d37c1e89656 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 23:20:28 +0200 Subject: [PATCH 12/15] Implemented missing constant in code --- lib/presentation/views/active_game_view.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index c3510a2..58d0885 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -455,7 +455,8 @@ class _ActiveGameViewState extends State { // If the previous round was not the last one if (round != null && round >= 0) { WidgetsBinding.instance.addPostFrameCallback((_) async { - await Future.delayed(const Duration(milliseconds: 600)); + await Future.delayed( + const Duration(milliseconds: Constants.roundViewDelay)); if (context.mounted) { _openRoundView(context, round); } From 36aad89952e4a3fb737cda915d6bb717c77cb03f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 23:29:25 +0200 Subject: [PATCH 13/15] Updated gameSession logic so more than one player can be winner --- lib/data/game_session.dart | 16 ++++++++++------ lib/presentation/views/active_game_view.dart | 8 ++++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/data/game_session.dart b/lib/data/game_session.dart index a9164be..3dcf4c2 100644 --- a/lib/data/game_session.dart +++ b/lib/data/game_session.dart @@ -299,15 +299,19 @@ class GameSession extends ChangeNotifier { /// It iterates through the player scores and finds the player /// with the lowest score. void _setWinner() { - int score = playerScores[0]; - String lowestPlayer = players[0]; + int minScore = playerScores.reduce((a, b) => a < b ? a : b); + List lowestPlayers = []; for (int i = 0; i < players.length; i++) { - if (playerScores[i] < score) { - score = playerScores[i]; - lowestPlayer = players[i]; + if (playerScores[i] == minScore) { + lowestPlayers.add(players[i]); } } - winner = lowestPlayer; + if (lowestPlayers.length > 1) { + winner = + '${lowestPlayers.sublist(0, lowestPlayers.length - 1).join(', ')} & ${lowestPlayers.last}'; + } else { + winner = lowestPlayers.first; + } notifyListeners(); } diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 58d0885..e1d1638 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -9,6 +9,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:collection/collection.dart'; import 'package:confetti/confetti.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -467,8 +468,7 @@ class _ActiveGameViewState extends State { /// Plays the confetti animation and shows a dialog with the winner's information. Future _playFinishAnimation(BuildContext context) async { String winner = widget.gameSession.winner; - int winnerIndex = widget.gameSession.players.indexOf(winner); - int points = widget.gameSession.playerScores[winnerIndex]; + int winnerPoints = widget.gameSession.playerScores.min; confettiController.play(); @@ -480,8 +480,8 @@ class _ActiveGameViewState extends State { 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)), + content: Text(AppLocalizations.of(context).end_of_game_message( + winner.contains('&') ? 2 : 1, winner, winnerPoints)), actions: [ CupertinoDialogAction( child: Text(AppLocalizations.of(context).ok), From 2bb9d82bfcdfcb5b37ed452b606373fed3a8757d Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 23:33:55 +0200 Subject: [PATCH 14/15] Updated strings --- lib/l10n/arb/app_en.arb | 2 +- lib/l10n/generated/app_localizations_en.dart | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 306768f..bedf95f 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -97,7 +97,7 @@ }, "end_of_game_title": "End of Game", - "end_of_game_message": "{playerCount, plural, =1{{names} won the game with {points} points. Congratulations!} other{{names} won the game with {points} points. Congratulations!}}", + "end_of_game_message": "{names} won the game with {points} points. Congratulations!", "@end_of_game_message": { "placeholders": { "playerCount": { diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 2da405c..837c83e 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -210,13 +210,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String end_of_game_message(int playerCount, String names, int points) { - String _temp0 = intl.Intl.pluralLogic( - playerCount, - locale: localeName, - other: '$names won the game with $points points. Congratulations!', - one: '$names won the game with $points points. Congratulations!', - ); - return '$_temp0'; + return '$names won the game with $points points. Congratulations!'; } @override From 1c7dd0a6337ee53c3f03f33abe2c0b4c393e3e5a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 20 Jul 2025 23:34:46 +0200 Subject: [PATCH 15/15] Updated winner popup --- lib/presentation/views/active_game_view.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index e1d1638..25e88a7 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -469,6 +469,7 @@ class _ActiveGameViewState extends State { Future _playFinishAnimation(BuildContext context) async { String winner = widget.gameSession.winner; int winnerPoints = widget.gameSession.playerScores.min; + int winnerAmount = winner.contains('&') ? 2 : 1; confettiController.play(); @@ -480,8 +481,8 @@ class _ActiveGameViewState extends State { builder: (BuildContext context) { return CupertinoAlertDialog( title: Text(AppLocalizations.of(context).end_of_game_title), - content: Text(AppLocalizations.of(context).end_of_game_message( - winner.contains('&') ? 2 : 1, winner, winnerPoints)), + content: Text(AppLocalizations.of(context) + .end_of_game_message(winnerAmount, winner, winnerPoints)), actions: [ CupertinoDialogAction( child: Text(AppLocalizations.of(context).ok),