Merge pull request #123 from flixcoo/feature/115-standard-rule

Standard rule
This commit is contained in:
2025-07-19 22:58:59 +02:00
committed by GitHub
11 changed files with 175 additions and 54 deletions

View File

@@ -58,6 +58,9 @@
"no_name_message": "Jeder Spieler muss einen Namen haben.", "no_name_message": "Jeder Spieler muss einen Namen haben.",
"select_game_mode": "Spielmodus auswählen", "select_game_mode": "Spielmodus auswählen",
"no_mode_selected": "Kein Modus ausgewählt",
"no_default_mode": "Kein Standard-Modus",
"no_default_description": "Der Standard-Modus wird zurückgesetzt.",
"point_limit_description": "Es wird so lange gespielt, bis ein:e Spieler:in mehr als {pointLimit} Punkte erreicht", "point_limit_description": "Es wird so lange gespielt, bis ein:e Spieler:in mehr als {pointLimit} Punkte erreicht",
"@point_limit_description": { "@point_limit_description": {
"placeholders": { "placeholders": {
@@ -109,9 +112,8 @@
"settings": "Einstellungen", "settings": "Einstellungen",
"cabo_penalty": "Cabo-Strafe", "cabo_penalty": "Cabo-Strafe",
"cabo_penalty_subtitle": "... für falsches Cabo sagen",
"point_limit": "Punkte-Limit", "point_limit": "Punkte-Limit",
"point_limit_subtitle": "... hier ist Schluss", "standard_mode": "Standard-Modus",
"reset_to_default": "Auf Standard zurücksetzen", "reset_to_default": "Auf Standard zurücksetzen",
"game_data": "Spieldaten", "game_data": "Spieldaten",
"import_data": "Spieldaten importieren", "import_data": "Spieldaten importieren",

View File

@@ -58,6 +58,9 @@
"no_name_message": "Each player must have a name.", "no_name_message": "Each player must have a name.",
"select_game_mode": "Select game mode", "select_game_mode": "Select game mode",
"no_mode_selected": "No mode selected",
"no_default_mode": "No default mode",
"no_default_description": "The default mode gets reset.",
"point_limit_description": "The game ends when a player scores more than {pointLimit} points.", "point_limit_description": "The game ends when a player scores more than {pointLimit} points.",
"@point_limit_description": { "@point_limit_description": {
"placeholders": { "placeholders": {
@@ -109,9 +112,8 @@
"settings": "Settings", "settings": "Settings",
"cabo_penalty": "Cabo Penalty", "cabo_penalty": "Cabo Penalty",
"cabo_penalty_subtitle": "A point penalty for incorrectly calling Cabo.",
"point_limit": "Point Limit", "point_limit": "Point Limit",
"point_limit_subtitle": "The required score to win the game.", "standard_mode": "Default Mode",
"reset_to_default": "Reset to Default", "reset_to_default": "Reset to Default",
"game_data": "Game Data", "game_data": "Game Data",
"import_data": "Import Data", "import_data": "Import Data",

View File

@@ -374,6 +374,24 @@ abstract class AppLocalizations {
/// **'Spielmodus auswählen'** /// **'Spielmodus auswählen'**
String get select_game_mode; String get select_game_mode;
/// No description provided for @no_mode_selected.
///
/// In de, this message translates to:
/// **'Kein Modus ausgewählt'**
String get no_mode_selected;
/// No description provided for @no_default_mode.
///
/// In de, this message translates to:
/// **'Kein Standard-Modus'**
String get no_default_mode;
/// No description provided for @no_default_description.
///
/// In de, this message translates to:
/// **'Der Standard-Modus wird zurückgesetzt.'**
String get no_default_description;
/// No description provided for @point_limit_description. /// No description provided for @point_limit_description.
/// ///
/// In de, this message translates to: /// In de, this message translates to:
@@ -519,23 +537,17 @@ abstract class AppLocalizations {
/// **'Cabo-Strafe'** /// **'Cabo-Strafe'**
String get cabo_penalty; String get cabo_penalty;
/// No description provided for @cabo_penalty_subtitle.
///
/// In de, this message translates to:
/// **'... für falsches Cabo sagen'**
String get cabo_penalty_subtitle;
/// No description provided for @point_limit. /// No description provided for @point_limit.
/// ///
/// In de, this message translates to: /// In de, this message translates to:
/// **'Punkte-Limit'** /// **'Punkte-Limit'**
String get point_limit; String get point_limit;
/// No description provided for @point_limit_subtitle. /// No description provided for @standard_mode.
/// ///
/// In de, this message translates to: /// In de, this message translates to:
/// **'... hier ist Schluss'** /// **'Standard-Modus'**
String get point_limit_subtitle; String get standard_mode;
/// No description provided for @reset_to_default. /// No description provided for @reset_to_default.
/// ///

View File

@@ -154,6 +154,15 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get select_game_mode => 'Spielmodus auswählen'; String get select_game_mode => 'Spielmodus auswählen';
@override
String get no_mode_selected => 'Kein Modus ausgewählt';
@override
String get no_default_mode => 'Kein Standard-Modus';
@override
String get no_default_description => 'Der Standard-Modus wird zurückgesetzt.';
@override @override
String point_limit_description(int pointLimit) { String point_limit_description(int pointLimit) {
return 'Es wird so lange gespielt, bis ein:e Spieler:in mehr als $pointLimit Punkte erreicht'; return 'Es wird so lange gespielt, bis ein:e Spieler:in mehr als $pointLimit Punkte erreicht';
@@ -243,14 +252,11 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get cabo_penalty => 'Cabo-Strafe'; String get cabo_penalty => 'Cabo-Strafe';
@override
String get cabo_penalty_subtitle => '... für falsches Cabo sagen';
@override @override
String get point_limit => 'Punkte-Limit'; String get point_limit => 'Punkte-Limit';
@override @override
String get point_limit_subtitle => '... hier ist Schluss'; String get standard_mode => 'Standard-Modus';
@override @override
String get reset_to_default => 'Auf Standard zurücksetzen'; String get reset_to_default => 'Auf Standard zurücksetzen';

View File

@@ -152,6 +152,15 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get select_game_mode => 'Select game mode'; String get select_game_mode => 'Select game mode';
@override
String get no_mode_selected => 'No mode selected';
@override
String get no_default_mode => 'No default mode';
@override
String get no_default_description => 'The default mode gets reset.';
@override @override
String point_limit_description(int pointLimit) { String point_limit_description(int pointLimit) {
return 'The game ends when a player scores more than $pointLimit points.'; return 'The game ends when a player scores more than $pointLimit points.';
@@ -241,15 +250,11 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get cabo_penalty => 'Cabo Penalty'; String get cabo_penalty => 'Cabo Penalty';
@override
String get cabo_penalty_subtitle =>
'A point penalty for incorrectly calling Cabo.';
@override @override
String get point_limit => 'Point Limit'; String get point_limit => 'Point Limit';
@override @override
String get point_limit_subtitle => 'The required score to win the game.'; String get standard_mode => 'Default Mode';
@override @override
String get reset_to_default => 'Reset to Default'; String get reset_to_default => 'Reset to Default';

View File

@@ -12,8 +12,6 @@ Future<void> main() async {
await SystemChrome.setPreferredOrientations( await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
await ConfigService.initConfig(); await ConfigService.initConfig();
ConfigService.pointLimit = await ConfigService.getPointLimit();
ConfigService.caboPenalty = await ConfigService.getCaboPenalty();
await VersionService.init(); await VersionService.init();
runApp(const App()); runApp(const App());
} }

View File

@@ -49,7 +49,13 @@ class _CreateGameViewState extends State<CreateGameView> {
void initState() { void initState() {
super.initState(); super.initState();
if (widget.isPointsLimitEnabled == null) {
_isPointsLimitEnabled =
ConfigService.gameMode == -1 ? null : ConfigService.gameMode == 1;
} else {
_isPointsLimitEnabled = widget.isPointsLimitEnabled; _isPointsLimitEnabled = widget.isPointsLimitEnabled;
}
_gameTitleTextController.text = widget.gameTitle ?? ''; _gameTitleTextController.text = widget.gameTitle ?? '';
if (widget.players != null) { if (widget.players != null) {
@@ -91,7 +97,6 @@ class _CreateGameViewState extends State<CreateGameView> {
controller: _gameTitleTextController, controller: _gameTitleTextController,
), ),
), ),
// Spielmodus-Auswahl mit Chevron
Padding( Padding(
padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), padding: const EdgeInsets.fromLTRB(15, 10, 10, 0),
child: CupertinoTextField( child: CupertinoTextField(
@@ -117,13 +122,26 @@ class _CreateGameViewState extends State<CreateGameView> {
CupertinoPageRoute( CupertinoPageRoute(
builder: (context) => ModeSelectionMenu( builder: (context) => ModeSelectionMenu(
pointLimit: ConfigService.pointLimit, pointLimit: ConfigService.pointLimit,
showDeselection: false,
), ),
), ),
); );
if (selectedMode != null) { switch (selectedMode) {
case GameMode.pointLimit:
setState(() { setState(() {
_isPointsLimitEnabled = selectedMode; _isPointsLimitEnabled = true;
});
break;
case GameMode.unlimited:
setState(() {
_isPointsLimitEnabled = false;
});
break;
case GameMode.none:
default:
setState(() {
_isPointsLimitEnabled = null;
}); });
} }
}, },
@@ -138,11 +156,9 @@ class _CreateGameViewState extends State<CreateGameView> {
), ),
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: _playerNameTextControllers.length + itemCount: _playerNameTextControllers.length + 1,
1, // +1 für den + Button
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == _playerNameTextControllers.length) { if (index == _playerNameTextControllers.length) {
// + Button als letztes Element
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0), padding: const EdgeInsets.symmetric(vertical: 8.0),
child: CupertinoButton( child: CupertinoButton(

View File

@@ -2,9 +2,17 @@ import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
enum GameMode {
none,
pointLimit,
unlimited,
}
class ModeSelectionMenu extends StatelessWidget { class ModeSelectionMenu extends StatelessWidget {
final int pointLimit; final int pointLimit;
const ModeSelectionMenu({super.key, required this.pointLimit}); final bool showDeselection;
const ModeSelectionMenu(
{super.key, required this.pointLimit, required this.showDeselection});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -26,12 +34,12 @@ class ModeSelectionMenu extends StatelessWidget {
maxLines: 3, maxLines: 3,
), ),
onTap: () { onTap: () {
Navigator.pop(context, true); Navigator.pop(context, GameMode.pointLimit);
}, },
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0), padding: const EdgeInsets.fromLTRB(0, 16, 0, 0),
child: CupertinoListTile( child: CupertinoListTile(
title: Text(AppLocalizations.of(context).unlimited, title: Text(AppLocalizations.of(context).unlimited,
style: CustomTheme.modeTitle), style: CustomTheme.modeTitle),
@@ -41,10 +49,27 @@ class ModeSelectionMenu extends StatelessWidget {
maxLines: 3, maxLines: 3,
), ),
onTap: () { onTap: () {
Navigator.pop(context, false); Navigator.pop(context, GameMode.unlimited);
}, },
), ),
), ),
Visibility(
visible: showDeselection,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 0),
child: CupertinoListTile(
title: Text(AppLocalizations.of(context).no_default_mode,
style: CustomTheme.modeTitle),
subtitle: Text(
AppLocalizations.of(context).no_default_description,
style: CustomTheme.modeDescription,
maxLines: 3,
),
onTap: () {
Navigator.pop(context, GameMode.none);
},
),
)),
], ],
), ),
); );

View File

@@ -1,6 +1,7 @@
import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/core/constants.dart';
import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/presentation/views/mode_selection_view.dart';
import 'package:cabo_counter/presentation/widgets/custom_form_row.dart'; import 'package:cabo_counter/presentation/widgets/custom_form_row.dart';
import 'package:cabo_counter/presentation/widgets/custom_stepper.dart'; import 'package:cabo_counter/presentation/widgets/custom_stepper.dart';
import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/config_service.dart';
@@ -20,6 +21,7 @@ class SettingsView extends StatefulWidget {
class _SettingsViewState extends State<SettingsView> { class _SettingsViewState extends State<SettingsView> {
UniqueKey _stepperKey1 = UniqueKey(); UniqueKey _stepperKey1 = UniqueKey();
UniqueKey _stepperKey2 = UniqueKey(); UniqueKey _stepperKey2 = UniqueKey();
int defaultMode = ConfigService.gameMode;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -62,7 +64,6 @@ class _SettingsViewState extends State<SettingsView> {
onChanged: (newCaboPenalty) { onChanged: (newCaboPenalty) {
setState(() { setState(() {
ConfigService.setCaboPenalty(newCaboPenalty); ConfigService.setCaboPenalty(newCaboPenalty);
ConfigService.caboPenalty = newCaboPenalty;
}); });
}, },
), ),
@@ -79,11 +80,59 @@ class _SettingsViewState extends State<SettingsView> {
onChanged: (newPointLimit) { onChanged: (newPointLimit) {
setState(() { setState(() {
ConfigService.setPointLimit(newPointLimit); ConfigService.setPointLimit(newPointLimit);
ConfigService.pointLimit = newPointLimit;
}); });
}, },
), ),
), ),
CustomFormRow(
prefixText: AppLocalizations.of(context).standard_mode,
prefixIcon: CupertinoIcons.square_stack,
suffixWidget: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
defaultMode == -1
? AppLocalizations.of(context)
.no_mode_selected
: (defaultMode == 1
? '${ConfigService.pointLimit} ${AppLocalizations.of(context).points}'
: AppLocalizations.of(context).unlimited),
),
const SizedBox(width: 10),
const CupertinoListTileChevron()
],
),
onPressed: () async {
final selectedMode = await Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => ModeSelectionMenu(
pointLimit: ConfigService.pointLimit,
showDeselection: true,
),
),
);
switch (selectedMode) {
case GameMode.pointLimit:
setState(() {
defaultMode = 1;
});
break;
case GameMode.unlimited:
setState(() {
defaultMode = 0;
});
break;
case GameMode.none:
default:
setState(() {
defaultMode = -1;
});
}
ConfigService.setGameMode(defaultMode);
},
),
CustomFormRow( CustomFormRow(
prefixText: prefixText:
AppLocalizations.of(context).reset_to_default, AppLocalizations.of(context).reset_to_default,
@@ -93,6 +142,7 @@ class _SettingsViewState extends State<SettingsView> {
setState(() { setState(() {
_stepperKey1 = UniqueKey(); _stepperKey1 = UniqueKey();
_stepperKey2 = UniqueKey(); _stepperKey2 = UniqueKey();
defaultMode = ConfigService.gameMode;
}); });
}, },
) )

View File

@@ -6,27 +6,35 @@ import 'package:shared_preferences/shared_preferences.dart';
class ConfigService { class ConfigService {
static const String _keyPointLimit = 'pointLimit'; static const String _keyPointLimit = 'pointLimit';
static const String _keyCaboPenalty = 'caboPenalty'; static const String _keyCaboPenalty = 'caboPenalty';
static const String _keyGameMode = 'gameMode';
// Actual values used in the app // Actual values used in the app
static int pointLimit = 100; static int pointLimit = 100;
static int caboPenalty = 5; static int caboPenalty = 5;
static int gameMode = -1;
// Default values // Default values
static const int _defaultPointLimit = 100; static const int _defaultPointLimit = 100;
static const int _defaultCaboPenalty = 5; static const int _defaultCaboPenalty = 5;
static const int _defaultGameMode = -1;
static Future<void> initConfig() async { static Future<void> initConfig() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
// Default values only set if they are not already set // Initialize pointLimit, caboPenalty, and gameMode from SharedPreferences
prefs.setInt( // If they are not set, use the default values
_keyPointLimit, prefs.getInt(_keyPointLimit) ?? _defaultPointLimit); pointLimit = prefs.getInt(_keyPointLimit) ?? _defaultPointLimit;
prefs.setInt( caboPenalty = prefs.getInt(_keyCaboPenalty) ?? _defaultCaboPenalty;
_keyCaboPenalty, prefs.getInt(_keyCaboPenalty) ?? _defaultCaboPenalty); gameMode = prefs.getInt(_keyGameMode) ?? _defaultGameMode;
// Save the initial values to SharedPreferences
prefs.setInt(_keyPointLimit, pointLimit);
prefs.setInt(_keyCaboPenalty, caboPenalty);
prefs.setInt(_keyGameMode, gameMode);
} }
/// Getter for the point limit. static Future<void> setGameMode(int newGameMode) async {
static Future<int> getPointLimit() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
return prefs.getInt(_keyPointLimit) ?? _defaultPointLimit; await prefs.setInt(_keyGameMode, newGameMode);
gameMode = newGameMode;
} }
/// Setter for the point limit. /// Setter for the point limit.
@@ -34,12 +42,7 @@ class ConfigService {
static Future<void> setPointLimit(int newPointLimit) async { static Future<void> setPointLimit(int newPointLimit) async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setInt(_keyPointLimit, newPointLimit); await prefs.setInt(_keyPointLimit, newPointLimit);
} pointLimit = newPointLimit;
/// Getter for the cabo penalty.
static Future<int> getCaboPenalty() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getInt(_keyCaboPenalty) ?? _defaultCaboPenalty;
} }
/// Setter for the cabo penalty. /// Setter for the cabo penalty.
@@ -47,12 +50,14 @@ class ConfigService {
static Future<void> setCaboPenalty(int newCaboPenalty) async { static Future<void> setCaboPenalty(int newCaboPenalty) async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setInt(_keyCaboPenalty, newCaboPenalty); await prefs.setInt(_keyCaboPenalty, newCaboPenalty);
caboPenalty = newCaboPenalty;
} }
/// Resets the configuration to default values. /// Resets the configuration to default values.
static Future<void> resetConfig() async { static Future<void> resetConfig() async {
ConfigService.pointLimit = _defaultPointLimit; ConfigService.pointLimit = _defaultPointLimit;
ConfigService.caboPenalty = _defaultCaboPenalty; ConfigService.caboPenalty = _defaultCaboPenalty;
ConfigService.gameMode = _defaultGameMode;
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setInt(_keyPointLimit, _defaultPointLimit); await prefs.setInt(_keyPointLimit, _defaultPointLimit);
await prefs.setInt(_keyCaboPenalty, _defaultCaboPenalty); await prefs.setInt(_keyCaboPenalty, _defaultCaboPenalty);

View File

@@ -2,7 +2,7 @@ name: cabo_counter
description: "Mobile app for the card game Cabo" description: "Mobile app for the card game Cabo"
publish_to: 'none' publish_to: 'none'
version: 0.4.9+533 version: 0.4.9+540
environment: environment:
sdk: ^3.5.4 sdk: ^3.5.4