diff --git a/lib/data/game_manager.dart b/lib/data/game_manager.dart new file mode 100644 index 0000000..578b03b --- /dev/null +++ b/lib/data/game_manager.dart @@ -0,0 +1,30 @@ +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/services/local_storage_service.dart'; +import 'package:flutter/foundation.dart'; + +class GameManager extends ChangeNotifier { + List gameList = []; + + /// Adds a new game session to the list and sorts it by creation date. + /// Takes a [GameSession] object as input. It then adds the session to the `gameList`, + /// sorts the list in descending order based on the creation date, and notifies listeners of the change. + /// It also saves the updated game sessions to local storage. + void addGameSession(GameSession session) { + gameList.add(session); + gameList.sort((a, b) => b.createdAt.compareTo(a.createdAt)); + notifyListeners(); + LocalStorageService.saveGameSessions(); + } + + /// Removes a game session from the list and sorts it by creation date. + /// Takes a [index] as input. It then removes the session at the specified index from the `gameList`, + /// sorts the list in descending order based on the creation date, and notifies listeners of the change. + /// It also saves the updated game sessions to local storage. + void removeGameSession(int index) { + gameList.removeAt(index); + notifyListeners(); + LocalStorageService.saveGameSessions(); + } +} + +final globals = GameManager(); diff --git a/lib/services/local_storage_service.dart b/lib/services/local_storage_service.dart index a68f3e4..a6d005c 100644 --- a/lib/services/local_storage_service.dart +++ b/lib/services/local_storage_service.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:io'; +import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; -import 'package:cabo_counter/utility/globals.dart'; import 'package:file_picker/file_picker.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/services.dart'; @@ -19,7 +19,7 @@ class LocalStorageService { /// Writes the game session list to a JSON file and returns it as string. static String getJsonFile() { final jsonFile = - Globals.gameList.map((session) => session.toJson()).toList(); + globals.gameList.map((session) => session.toJson()).toList(); return json.encode(jsonFile); } @@ -63,14 +63,14 @@ class LocalStorageService { if (!await validateJsonSchema(jsonString)) { logger.w('Die Datei konnte nicht validiert werden'); - Globals.gameList = []; + globals.gameList = []; return false; } logger.d('Die gefundene Datei hat Inhalt'); logger.d('Die gefundene Datei wurde erfolgreich validiert'); final jsonList = json.decode(jsonString) as List; - Globals.gameList = jsonList + globals.gameList = jsonList .map((jsonItem) => GameSession.fromJson(jsonItem as Map)) .toList(); @@ -80,7 +80,7 @@ class LocalStorageService { } catch (e) { logger.e('Fehler beim Laden der Spieldaten:\n$e', error: 'JSON nicht geladen'); - Globals.gameList = []; + globals.gameList = []; return false; } } @@ -125,7 +125,7 @@ class LocalStorageService { return false; } final jsonData = json.decode(jsonString) as List; - Globals.gameList = jsonData + globals.gameList = jsonData .map((jsonItem) => GameSession.fromJson(jsonItem as Map)) .toList(); @@ -172,7 +172,7 @@ class LocalStorageService { static Future deleteAllGames() async { try { - Globals.gameList.clear(); + globals.gameList.clear(); await saveGameSessions(); logger.i('Alle Runden wurden erfolgreich gelöscht.'); return true; diff --git a/lib/utility/globals.dart b/lib/utility/globals.dart index 89d8559..4244f07 100644 --- a/lib/utility/globals.dart +++ b/lib/utility/globals.dart @@ -1,17 +1,5 @@ -import 'package:cabo_counter/data/game_session.dart'; - class Globals { - /// The [gameList] contains all active game sessions. - static List gameList = []; - - static void addGameSession(GameSession session) { - gameList.add(session); - gameList.sort((a, b) => b.createdAt.compareTo(a.createdAt)); - } - static int pointLimit = 100; - static int caboPenalty = 5; - static String appDevPhase = 'Alpha'; } diff --git a/lib/views/create_game_view.dart b/lib/views/create_game_view.dart index 7bbeefa..92ce4f0 100644 --- a/lib/views/create_game_view.dart +++ b/lib/views/create_game_view.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; @@ -289,9 +290,7 @@ class _CreateGameState extends State { caboPenalty: Globals.caboPenalty, isPointsLimitEnabled: selectedMode!, ); - setState(() { - Globals.addGameSession(gameSession); - }); + globals.addGameSession(gameSession); LocalStorageService.saveGameSessions(); if (context.mounted) { Navigator.pushReplacement( diff --git a/lib/views/main_menu_view.dart b/lib/views/main_menu_view.dart index fabd02d..0420a4c 100644 --- a/lib/views/main_menu_view.dart +++ b/lib/views/main_menu_view.dart @@ -1,6 +1,6 @@ +import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/utility/globals.dart'; import 'package:cabo_counter/views/active_game_view.dart'; import 'package:cabo_counter/views/create_game_view.dart'; import 'package:cabo_counter/views/settings_view.dart'; @@ -26,6 +26,11 @@ class _MainMenuViewState extends State { _isLoading = false; }); }); + globals.addListener(_updateView); + } + + void _updateView() { + if (mounted) setState(() {}); } @override @@ -33,132 +38,143 @@ class _MainMenuViewState extends State { print('MainMenuView build'); LocalStorageService.loadGameSessions(); - return CupertinoPageScaffold( - resizeToAvoidBottomInset: false, - navigationBar: CupertinoNavigationBar( - leading: IconButton( - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => const SettingsView(), - ), - ); - }, - icon: const Icon(CupertinoIcons.settings, size: 30)), - middle: const Text('Cabo Counter'), - trailing: IconButton( - onPressed: () => { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => const CreateGame(), - ), - ) - }, - icon: const Icon(CupertinoIcons.add)), - ), - child: CupertinoPageScaffold( - child: SafeArea( - child: _isLoading - ? const Center(child: CupertinoActivityIndicator()) - : Globals.gameList.isEmpty - ? Column( - mainAxisAlignment: - MainAxisAlignment.center, // Oben ausrichten - children: [ - const SizedBox(height: 30), // Abstand von oben - Center( - child: GestureDetector( - onTap: () => setState(() {}), - child: Icon( - CupertinoIcons.plus, - size: 60, - color: CustomTheme.primaryColor, + return ListenableBuilder( + listenable: globals, + builder: (context, _) { + return CupertinoPageScaffold( + resizeToAvoidBottomInset: false, + navigationBar: CupertinoNavigationBar( + leading: IconButton( + onPressed: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => const SettingsView(), + ), + ); + }, + icon: const Icon(CupertinoIcons.settings, size: 30)), + middle: const Text('Cabo Counter'), + trailing: IconButton( + onPressed: () => { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => const CreateGame(), ), - )), - const SizedBox(height: 10), // Abstand von oben - const Padding( - padding: EdgeInsets.symmetric(horizontal: 70), - child: Text( - 'Ganz schön leer hier...\nFüge über den Button oben rechts eine neue Runde hinzu.', - textAlign: TextAlign.center, - style: TextStyle(fontSize: 16), - ), - ), - ], - ) - : ListView.builder( - itemCount: Globals.gameList.length, - itemBuilder: (context, index) { - final session = Globals.gameList[index]; - return Dismissible( - key: Key(session.gameTitle), - background: Container( - color: CupertinoColors.destructiveRed, - alignment: Alignment.centerLeft, - padding: const EdgeInsets.only(left: 20.0), - child: const Icon( - CupertinoIcons.delete, - color: CupertinoColors.white, - ), - ), - direction: DismissDirection.startToEnd, - confirmDismiss: (direction) async { - final String gameTitle = - Globals.gameList[index].gameTitle; - return await _showDeleteGamePopup(gameTitle); - }, - onDismissed: (direction) { - _deleteSpecificGame(index); - }, - dismissThresholds: const { - DismissDirection.startToEnd: 0.6 - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10.0), - child: CupertinoListTile( - title: Text(session.gameTitle), - subtitle: session.isGameFinished == true - ? Text( - '\u{1F947} ${session.winner}', - style: const TextStyle(fontSize: 14), - ) - : Text( - 'Modus: ${_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: () async { - //ignore: unused_local_variable - final val = await Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ActiveGameView( - gameSession: Globals.gameList[index]), - ), - ); - setState(() {}); - }, - ), - ), - ); + ) }, - ), - ), - ), - ); + icon: const Icon(CupertinoIcons.add)), + ), + child: CupertinoPageScaffold( + child: SafeArea( + child: _isLoading + ? const Center(child: CupertinoActivityIndicator()) + : globals.gameList.isEmpty + ? Column( + mainAxisAlignment: + MainAxisAlignment.center, // Oben ausrichten + children: [ + const SizedBox(height: 30), // Abstand von oben + Center( + child: GestureDetector( + onTap: () => setState(() {}), + child: Icon( + CupertinoIcons.plus, + size: 60, + color: CustomTheme.primaryColor, + ), + )), + const SizedBox(height: 10), // Abstand von oben + const Padding( + padding: EdgeInsets.symmetric(horizontal: 70), + child: Text( + 'Ganz schön leer hier...\nFüge über den Button oben rechts eine neue Runde hinzu.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 16), + ), + ), + ], + ) + : ListView.builder( + itemCount: globals.gameList.length, + itemBuilder: (context, index) { + final session = globals.gameList[index]; + return Dismissible( + key: Key(session.gameTitle), + background: Container( + color: CupertinoColors.destructiveRed, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 20.0), + child: const Icon( + CupertinoIcons.delete, + color: CupertinoColors.white, + ), + ), + direction: DismissDirection.startToEnd, + confirmDismiss: (direction) async { + final String gameTitle = + globals.gameList[index].gameTitle; + return await _showDeleteGamePopup(gameTitle); + }, + onDismissed: (direction) { + _deleteSpecificGame(index); + }, + 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( + 'Modus: ${_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: () async { + //ignore: unused_local_variable + final val = await Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ActiveGameView( + gameSession: + globals.gameList[index]), + ), + ); + setState(() {}); + }, + ), + ), + ); + }, + ), + ), + ), + ); + }); } /// Translates the game mode boolean into the corresponding String. @@ -204,7 +220,7 @@ class _MainMenuViewState extends State { /// This function takes an [index] as parameter and removes the game session at /// that index from the global game list, void _deleteSpecificGame(int index) { - Globals.gameList.removeAt(index); + globals.gameList.removeAt(index); LocalStorageService.saveGameSessions(); } } diff --git a/lib/views/round_view.dart b/lib/views/round_view.dart index 48bc4b9..6cd6695 100644 --- a/lib/views/round_view.dart +++ b/lib/views/round_view.dart @@ -58,6 +58,7 @@ class _RoundViewState extends State { _kamikazePlayerIndex = gameSession.roundList[widget.roundNumber - 1].kamikazePlayerIndex; } + super.initState(); } @@ -215,6 +216,7 @@ class _RoundViewState extends State { textAlign: TextAlign.center, onSubmitted: (_) => _focusNextTextfield(index), + onChanged: (_) => setState(() {}), ), ), const SizedBox(width: 50), diff --git a/pubspec.yaml b/pubspec.yaml index 9661bec..1b9467e 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.1.6+145 +version: 0.1.6+149 environment: sdk: ^3.5.4