diff --git a/lib/core/custom_theme.dart b/lib/core/custom_theme.dart index fa78cb6..bfc4f3c 100644 --- a/lib/core/custom_theme.dart +++ b/lib/core/custom_theme.dart @@ -4,7 +4,9 @@ class CustomTheme { static Color white = CupertinoColors.white; static Color primaryColor = CupertinoColors.systemGreen; static Color backgroundColor = const Color(0xFF101010); - static Color backgroundTintColor = CupertinoColors.darkBackgroundGray; + static Color mainElementBackgroundColor = CupertinoColors.darkBackgroundGray; + static Color playerTileColor = CupertinoColors.secondaryLabel; + static Color buttonBackgroundColor = const Color(0xFF202020); // Line Colors for GraphView static const Color graphColor1 = Color(0xFFF44336); diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 399aefd..dceb8fc 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -71,6 +71,7 @@ "results": "Ergebnisse", "who_said_cabo": "Wer hat CABO gesagt?", "kamikaze": "Kamikaze", + "who_has_kamikaze": "Wer hat Kamikaze?", "done": "Fertig", "next_round": "Nächste Runde", "bonus_points_title": "Bonus-Punkte!", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 3b6150a..3009d6e 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -71,6 +71,7 @@ "results": "Results", "who_said_cabo": "Who called Cabo?", "kamikaze": "Kamikaze", + "who_has_kamikaze": "Who has Kamikaze?", "done": "Done", "next_round": "Next Round", "bonus_points_title": "Bonus-Points!", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 7ce3c52..695fb57 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -404,6 +404,12 @@ abstract class AppLocalizations { /// **'Kamikaze'** String get kamikaze; + /// No description provided for @who_has_kamikaze. + /// + /// In de, this message translates to: + /// **'Wer hat Kamikaze?'** + String get who_has_kamikaze; + /// No description provided for @done. /// /// 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 6539c20..3ef54b8 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -172,6 +172,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get kamikaze => 'Kamikaze'; + @override + String get who_has_kamikaze => 'Wer hat Kamikaze?'; + @override String get done => 'Fertig'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 7e026f6..8a86b95 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -170,6 +170,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get kamikaze => 'Kamikaze'; + @override + String get who_has_kamikaze => 'Who has Kamikaze?'; + @override String get done => 'Done'; diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 9e2b40f..f99380e 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -74,21 +74,22 @@ class _RoundViewState extends State { return CupertinoPageScaffold( resizeToAvoidBottomInset: false, navigationBar: CupertinoNavigationBar( - transitionBetweenRoutes: true, - leading: CupertinoButton( - padding: EdgeInsets.zero, - onPressed: () => - {LocalStorageService.saveGameSessions(), Navigator.pop(context)}, - child: Text(AppLocalizations.of(context).cancel), - ), - middle: Text(AppLocalizations.of(context).results), - trailing: widget.gameSession.isGameFinished - ? const Icon( + transitionBetweenRoutes: true, + leading: CupertinoButton( + padding: EdgeInsets.zero, + onPressed: () => { + LocalStorageService.saveGameSessions(), + Navigator.pop(context) + }, + child: Text(AppLocalizations.of(context).cancel), + ), + middle: Text(AppLocalizations.of(context).results), + trailing: Visibility( + visible: widget.gameSession.isGameFinished, + child: const Icon( CupertinoIcons.lock, size: 25, - ) - : null, - ), + ))), child: Stack( children: [ Positioned.fill( @@ -114,9 +115,10 @@ class _RoundViewState extends State { vertical: 10, ), child: SizedBox( - height: 40, + height: 60, child: CupertinoSegmentedControl( - unselectedColor: CustomTheme.backgroundTintColor, + unselectedColor: + CustomTheme.mainElementBackgroundColor, selectedColor: CustomTheme.primaryColor, groupValue: _caboPlayerIndex, children: Map.fromEntries(widget.gameSession.players @@ -130,7 +132,7 @@ class _RoundViewState extends State { Padding( padding: const EdgeInsets.symmetric( horizontal: 6, - vertical: 6, + vertical: 8, ), child: FittedBox( fit: BoxFit.scaleDown, @@ -154,27 +156,6 @@ class _RoundViewState extends State { ), ), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: CupertinoListTile( - title: Text(AppLocalizations.of(context).player), - trailing: Row( - children: [ - SizedBox( - width: 100, - child: Center( - child: Text( - AppLocalizations.of(context).points))), - const SizedBox(width: 20), - SizedBox( - width: 80, - child: Center( - child: Text(AppLocalizations.of(context) - .kamikaze))), - ], - ), - ), - ), ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -190,7 +171,7 @@ class _RoundViewState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(12), child: CupertinoListTile( - backgroundColor: CupertinoColors.secondaryLabel, + backgroundColor: CustomTheme.playerTileColor, title: Row(children: [ Expanded( child: Row(children: [ @@ -204,90 +185,68 @@ class _RoundViewState extends State { ), Visibility( visible: shouldShowMedal, - child: const Icon(FontAwesomeIcons.medal, + child: const Icon(FontAwesomeIcons.crown, size: 15)) ])) ]), subtitle: Text( '${widget.gameSession.playerScores[originalIndex]}' ' ${AppLocalizations.of(context).points}'), - trailing: Row( - children: [ - SizedBox( - width: 100, - child: CupertinoTextField( - maxLength: 3, - focusNode: _focusNodeList[originalIndex], - keyboardType: - const TextInputType.numberWithOptions( - signed: true, - decimal: false, - ), - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - textInputAction: index == - widget.gameSession.players - .length - - 1 - ? TextInputAction.done - : TextInputAction.next, - controller: - _scoreControllerList[originalIndex], - placeholder: - AppLocalizations.of(context).points, - textAlign: TextAlign.center, - onSubmitted: (_) => - _focusNextTextfield(originalIndex), - onChanged: (_) => setState(() {}), - ), + trailing: SizedBox( + width: 100, + child: CupertinoTextField( + maxLength: 3, + focusNode: _focusNodeList[originalIndex], + keyboardType: + const TextInputType.numberWithOptions( + signed: true, + decimal: false, ), - const SizedBox(width: 50), - GestureDetector( - onTap: () { - setState(() { - _kamikazePlayerIndex = - (_kamikazePlayerIndex == - originalIndex) - ? null - : originalIndex; - }); - }, - child: Container( - width: 24, - height: 24, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: _kamikazePlayerIndex == - originalIndex - ? CupertinoColors.systemRed - : CupertinoColors - .tertiarySystemFill, - border: Border.all( - color: _kamikazePlayerIndex == - originalIndex - ? CupertinoColors.systemRed - : CupertinoColors.systemGrey, - ), - ), - child: _kamikazePlayerIndex == - originalIndex - ? const Icon( - CupertinoIcons.exclamationmark, - size: 16, - color: CupertinoColors.white, - ) - : null, - ), - ), - const SizedBox(width: 22), - ], + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + textInputAction: index == + widget.gameSession.players.length - 1 + ? TextInputAction.done + : TextInputAction.next, + controller: + _scoreControllerList[originalIndex], + placeholder: + AppLocalizations.of(context).points, + textAlign: TextAlign.center, + onSubmitted: (_) => + _focusNextTextfield(originalIndex), + onChanged: (_) => setState(() {}), + ), ), ), ), ); }, ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 0, 0), + child: Center( + heightFactor: 1, + child: CupertinoButton( + sizeStyle: CupertinoButtonSize.medium, + borderRadius: BorderRadius.circular(12), + color: CustomTheme.buttonBackgroundColor, + onPressed: () async { + if (await _showKamikazeSheet(context)) { + if (!context.mounted) return; + _endOfRoundNavigation(context, true); + } + }, + child: Text( + AppLocalizations.of(context).kamikaze, + style: const TextStyle( + color: CupertinoColors.destructiveRed, + ), + ), + ), + ), + ), ], ), ), @@ -302,21 +261,14 @@ class _RoundViewState extends State { return Container( height: 80, padding: const EdgeInsets.only(bottom: 20), - color: CustomTheme.backgroundTintColor, + color: CustomTheme.mainElementBackgroundColor, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ CupertinoButton( onPressed: _areRoundInputsValid() - ? () async { - List bonusPlayersIndices = _finishRound(); - if (bonusPlayersIndices.isNotEmpty) { - await _showBonusPopup( - context, bonusPlayersIndices); - } - LocalStorageService.saveGameSessions(); - if (!context.mounted) return; - Navigator.pop(context); + ? () { + _endOfRoundNavigation(context, false); } : null, child: Text(AppLocalizations.of(context).done), @@ -324,21 +276,8 @@ class _RoundViewState extends State { if (!widget.gameSession.isGameFinished) CupertinoButton( onPressed: _areRoundInputsValid() - ? () async { - List bonusPlayersIndices = - _finishRound(); - if (bonusPlayersIndices.isNotEmpty) { - await _showBonusPopup( - context, bonusPlayersIndices); - } - LocalStorageService.saveGameSessions(); - if (widget.gameSession.isGameFinished && - context.mounted) { - Navigator.pop(context); - } else if (context.mounted) { - Navigator.pop( - context, widget.roundNumber + 1); - } + ? () { + _endOfRoundNavigation(context, true); } : null, child: Text(AppLocalizations.of(context).next_round), @@ -401,6 +340,37 @@ class _RoundViewState extends State { ]; } + /// Shows a Cupertino action sheet to select the player who has Kamikaze. + /// It returns true if a player was selected, false if the action was cancelled. + Future _showKamikazeSheet(BuildContext context) async { + return await showCupertinoModalPopup( + context: context, + builder: (BuildContext context) { + return CupertinoActionSheet( + title: Text(AppLocalizations.of(context).kamikaze), + message: Text(AppLocalizations.of(context).who_has_kamikaze), + actions: widget.gameSession.players.asMap().entries.map((entry) { + final index = entry.key; + final name = entry.value; + return CupertinoActionSheetAction( + onPressed: () { + _kamikazePlayerIndex = index; + Navigator.pop(context, true); + }, + child: Text(name), + ); + }).toList(), + cancelButton: CupertinoActionSheetAction( + onPressed: () => Navigator.pop(context, false), + isDestructiveAction: true, + child: Text(AppLocalizations.of(context).cancel), + ), + ); + }, + ) ?? + false; + } + /// Focuses the next text field in the list of text fields. /// [index] is the index of the current text field. void _focusNextTextfield(int index) { @@ -471,10 +441,9 @@ class _RoundViewState extends State { return bonusPlayers; } - /// Shows a popup dialog with the bonus information. + /// Shows a popup dialog with the information which player received the bonus points. Future _showBonusPopup( BuildContext context, List bonusPlayers) async { - print('Bonus Popup wird angezeigt'); int pointLimit = widget.gameSession.pointLimit; int bonusPoints = (pointLimit / 2).round(); @@ -521,6 +490,37 @@ class _RoundViewState extends State { return resultText; } + /// Handles the navigation for the end of the round. + /// It checks for bonus players and shows a popup, saves the game session, + /// and navigates to the next round or back to the previous screen. + /// It takes the BuildContext [context] and a boolean [navigateToNextRound] to determine + /// if it should navigate to the next round or not. + Future _endOfRoundNavigation( + BuildContext context, bool navigateToNextRound) async { + List bonusPlayersIndices = _finishRound(); + if (bonusPlayersIndices.isNotEmpty) { + await _showBonusPopup(context, bonusPlayersIndices); + } + + LocalStorageService.saveGameSessions(); + + if (context.mounted) { + // If the game is finished, pop the context and return to the previous screen. + if (widget.gameSession.isGameFinished) { + Navigator.pop(context); + return; + } + // If navigateToNextRound is false, pop the context and return to the previous screen. + if (!navigateToNextRound) { + Navigator.pop(context); + return; + } + // If navigateToNextRound is true and the game isn't finished yet, + // pop the context and navigate to the next round. + Navigator.pop(context, widget.roundNumber + 1); + } + } + @override void dispose() { for (final controller in _scoreControllerList) { diff --git a/lib/presentation/views/tab_view.dart b/lib/presentation/views/tab_view.dart index 0c98cc7..4b757fa 100644 --- a/lib/presentation/views/tab_view.dart +++ b/lib/presentation/views/tab_view.dart @@ -17,7 +17,7 @@ class _TabViewState extends State { Widget build(BuildContext context) { return CupertinoTabScaffold( tabBar: CupertinoTabBar( - backgroundColor: CustomTheme.backgroundTintColor, + backgroundColor: CustomTheme.mainElementBackgroundColor, iconSize: 27, height: 55, items: [ diff --git a/pubspec.yaml b/pubspec.yaml index 7f8483c..37af518 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.4.8+525 +version: 0.4.9+533 environment: sdk: ^3.5.4