diff --git a/lib/data/game_session.dart b/lib/data/game_session.dart index 0403822..36629f8 100644 --- a/lib/data/game_session.dart +++ b/lib/data/game_session.dart @@ -3,7 +3,7 @@ import 'package:cabo_counter/data/round.dart'; /// This class represents a game session for Cabo game. /// [createdAt] is the timestamp of when the game session was created. /// [gameTitle] is the title of the game. -/// [gameHasPointLimit] is a boolean indicating if the game has the default +/// [isPointsLimitEnabled] is a boolean indicating if the game has the default /// point limit of 101 points or not. /// [players] is a string list of player names. /// [playerScores] is a list of the summed scores of all players. @@ -13,7 +13,7 @@ import 'package:cabo_counter/data/round.dart'; class GameSession { final DateTime createdAt = DateTime.now(); final String gameTitle; - final bool gameHasPointLimit; + final bool isPointsLimitEnabled; final List players; late List playerScores; List roundList = []; @@ -23,21 +23,45 @@ class GameSession { GameSession({ required this.gameTitle, - required this.gameHasPointLimit, + required this.isPointsLimitEnabled, required this.players, }) { playerScores = List.filled(players.length, 0); } @override - String toString() { + toString() { return ('GameSession: [createdAt: $createdAt, gameTitle: $gameTitle, ' - 'gameHasPointLimit: $gameHasPointLimit, players: $players, ' + 'isPointsLimitEnabled: $isPointsLimitEnabled, players: $players, ' 'playerScores: $playerScores, roundList: $roundList, ' 'roundNumber: $roundNumber, isGameFinished: $isGameFinished, ' 'winner: $winner]'); } + /// Converts the GameSession object to a JSON map. + Map toJson() => { + 'gameTitle': gameTitle, + 'gameHasPointLimit': isPointsLimitEnabled, + 'players': players, + 'playerScores': playerScores, + 'roundNumber': roundNumber, + 'isGameFinished': isGameFinished, + 'winner': winner, + 'roundList': roundList.map((e) => e.toJson()).toList() + }; + + /// Creates a GameSession object from a JSON map. + GameSession.fromJson(Map json) + : gameTitle = json['gameTitle'], + isPointsLimitEnabled = json['gameHasPointLimit'], + players = List.from(json['players']), + playerScores = List.from(json['playerScores']), + roundNumber = json['roundNumber'], + isGameFinished = json['isGameFinished'], + winner = json['winner'], + roundList = + (json['roundList'] as List).map((e) => Round.fromJson(e)).toList(); + /// Returns the length of all player names combined. int getLengthOfPlayerNames() { int length = 0; @@ -184,7 +208,7 @@ class GameSession { /// the winner. void updatePoints() { _sumPoints(); - if (gameHasPointLimit) { + if (isPointsLimitEnabled) { _checkHundredPointsReached(); for (int i = 0; i < playerScores.length; i++) { diff --git a/lib/data/round.dart b/lib/data/round.dart index 1f03b33..0c5b456 100644 --- a/lib/data/round.dart +++ b/lib/data/round.dart @@ -1,3 +1,5 @@ +import 'package:cabo_counter/data/game_session.dart'; + /// This class represents a single round in the game. /// It is stored within the [GameSession] class. /// [roundNum] is the number of the round its reppresenting. @@ -16,4 +18,25 @@ class Round { required this.scores, required this.scoreUpdates, this.kamikazePlayerIndex}); + + @override + toString() { + return 'Round $roundNum: scores: $scores, scoreUpdates: $scoreUpdates, ' + 'kamikazePlayerIndex: $kamikazePlayerIndex'; + } + + /// Converts the Round object to a JSON map. + Map toJson() => { + 'roundNum': roundNum, + 'scores': scores, + 'scoreUpdates': scoreUpdates, + 'kamikazePlayerIndex': kamikazePlayerIndex, + }; + + /// Creates a Round object from a JSON map. + Round.fromJson(Map json) + : roundNum = json['roundNum'], + scores = List.from(json['scores']), + scoreUpdates = List.from(json['scoreUpdates']), + kamikazePlayerIndex = json['kamikazePlayerIndex']; } diff --git a/lib/main.dart b/lib/main.dart index 9580339..3ef2867 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,23 +1,90 @@ -import 'package:cabo_counter/utility/theme.dart' as theme; +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/utility/apptheme.dart'; +import 'package:cabo_counter/utility/globals.dart'; +import 'package:cabo_counter/utility/local_storage_service.dart'; import 'package:cabo_counter/views/main_menu_view.dart'; import 'package:flutter/cupertino.dart'; void main() { + /// FIXME Just for Debugging + /// Fills the game list with some test data. + Globals.addGameSession(GameSession( + gameTitle: 'Spiel am 27.02.2025', + players: ['Clara', 'Tobias', 'Yannik', 'Lena', 'Lekaia'], + isPointsLimitEnabled: true)); + Globals.addGameSession(GameSession( + gameTitle: 'Freundschaftsrunde', + players: ['Felix', 'Jonas', 'Nils'], + isPointsLimitEnabled: false)); + Globals.addGameSession(GameSession( + gameTitle: 'Familienabend', + players: ['Mama', 'Papa', 'Lisa'], + isPointsLimitEnabled: true, + )); + Globals.addGameSession(GameSession( + gameTitle: 'Turnier 1. Runde', + players: ['Tim', 'Max', 'Sophie', 'Lena'], + isPointsLimitEnabled: false)); + Globals.addGameSession(GameSession( + gameTitle: '2 Namen max length', + players: ['Heinrich', 'Johannes'], + isPointsLimitEnabled: true)); + Globals.addGameSession(GameSession( + gameTitle: '3 Namen max length', + players: ['Benjamin', 'Stefanie', 'Wolfgang'], + isPointsLimitEnabled: false)); + Globals.addGameSession(GameSession( + gameTitle: '4 Namen max length', + players: ['Leonhard', 'Mathilde', 'Bernhard', 'Gerlinde'], + isPointsLimitEnabled: true)); + Globals.addGameSession(GameSession( + gameTitle: '5 Namen max length', + players: ['Hartmuth', 'Elisabet', 'Rosalind', 'Theresia', 'Karoline'], + isPointsLimitEnabled: false)); + runApp(const App()); } -class App extends StatelessWidget { +class App extends StatefulWidget { const App({super.key}); + @override + State createState() => _AppState(); +} + +class _AppState extends State with WidgetsBindingObserver { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + LocalStorageService.loadGameSessions(); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.paused || + state == AppLifecycleState.detached) { + LocalStorageService.saveGameSessions(); + } + } + @override Widget build(BuildContext context) { + LocalStorageService.loadGameSessions(); + return CupertinoApp( theme: CupertinoThemeData( brightness: Brightness.dark, - primaryColor: theme.primaryColor, - scaffoldBackgroundColor: theme.backgroundColor, + primaryColor: AppTheme.primaryColor, + scaffoldBackgroundColor: AppTheme.backgroundColor, textTheme: CupertinoTextThemeData( - primaryColor: theme.primaryColor, + primaryColor: AppTheme.primaryColor, ), ), debugShowCheckedModeBanner: false, diff --git a/lib/utility/styles.dart b/lib/utility/apptheme.dart similarity index 63% rename from lib/utility/styles.dart rename to lib/utility/apptheme.dart index d0f2921..ebbbab6 100644 --- a/lib/utility/styles.dart +++ b/lib/utility/apptheme.dart @@ -1,8 +1,10 @@ import 'package:flutter/cupertino.dart'; -abstract class Styles { +class AppTheme { + static Color white = CupertinoColors.white; static Color primaryColor = CupertinoColors.systemGreen; - static Color backgroundColor = const Color(0xFF080808); + static Color backgroundColor = const Color(0xFF101010); + static Color backgroundTintColor = CupertinoColors.darkBackgroundGray; static TextStyle modeTitle = TextStyle( color: primaryColor, @@ -20,15 +22,15 @@ abstract class Styles { fontWeight: FontWeight.bold, ); - static TextStyle roundTitle = const TextStyle( + static TextStyle roundTitle = TextStyle( fontSize: 60, - color: CupertinoColors.white, + color: white, fontWeight: FontWeight.bold, ); - static TextStyle roundPlayers = const TextStyle( + static TextStyle roundPlayers = TextStyle( fontSize: 20, - color: CupertinoColors.white, + color: white, fontWeight: FontWeight.bold, ); } diff --git a/lib/utility/globals.dart b/lib/utility/globals.dart index 2d22891..62e64bd 100644 --- a/lib/utility/globals.dart +++ b/lib/utility/globals.dart @@ -1,5 +1,11 @@ import 'package:cabo_counter/data/game_session.dart'; class Globals { - static Map gamesMap = {}; + /// 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)); + } } diff --git a/lib/utility/local_storage_service.dart b/lib/utility/local_storage_service.dart new file mode 100644 index 0000000..661b139 --- /dev/null +++ b/lib/utility/local_storage_service.dart @@ -0,0 +1,121 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +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:path_provider/path_provider.dart'; + +class LocalStorageService { + static const String _fileName = 'game_data.json'; + + /// 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(); + return json.encode(jsonFile); + } + + /// Returns the path to the local JSON file. + static Future _getFilePath() async { + final directory = await getApplicationDocumentsDirectory(); + final path = '${directory.path}/$_fileName'; + return File(path); + } + + /// Saves the game sessions to a local JSON file. + static Future saveGameSessions() async { + try { + final file = await _getFilePath(); + final jsonFile = getJsonFile(); + await file.writeAsString(jsonFile); + print('Daten gespeichert'); + } catch (e) { + print('Fehler beim Speichern: $e'); + } + } + + /// Loads the game data from a local JSON file. + static Future loadGameSessions() async { + print('Versuche, Daten zu laden...'); + try { + final file = await _getFilePath(); + if (await file.exists()) { + print('Es existiert bereits eine Datei mit Spieldaten'); + final jsonString = await file.readAsString(); + if (jsonString.isNotEmpty) { + print('Die gefundene Datei ist nicht leer'); + final jsonList = json.decode(jsonString) as List; + Globals.gameList = jsonList + .map((jsonItem) => + GameSession.fromJson(jsonItem as Map)) + .toList() + .cast(); + print('Die Daten wurden erfolgreich geladen'); + } else { + print('Die Datei ist leer'); + } + } else { + print('Es existiert bisher noch keine Datei mit Spieldaten'); + } + } catch (e) { + print('Fehler beim Laden der Spieldaten:\n$e'); + Globals.gameList = []; + } + } + + /// Opens the file picker to save a JSON file with the current game data. + static Future exportJsonFile() async { + final jsonString = getJsonFile(); + try { + final bytes = Uint8List.fromList(utf8.encode(jsonString)); + final result = await FileSaver.instance.saveAs( + name: 'cabo_counter_data', + bytes: bytes, + ext: 'json', + mimeType: MimeType.json, + ); + print('Datei gespeichert: $result'); + return true; + } catch (e) { + print('Fehler beim Speichern: $e'); + return false; + } + } + + /// Opens the file picker to import a JSON file and loads the game data from it. + static Future importJsonFile() async { + try { + final result = await FilePicker.platform.pickFiles( + dialogTitle: 'Wähle eine Datei mit Spieldaten aus', + type: FileType.custom, + allowedExtensions: ['json'], + ); + String jsonString = ''; + if (result != null) { + if (result.files.single.bytes != null) { + final Uint8List fileBytes = result.files.single.bytes!; + jsonString = utf8.decode(fileBytes); + } else if (result.files.single.path != null) { + final file = File(result.files.single.path!); + jsonString = await file.readAsString(); + } + final jsonList = json.decode(jsonString) as List; + print('JSON Inhalt: $jsonList'); + Globals.gameList = jsonList + .map((jsonItem) => + GameSession.fromJson(jsonItem as Map)) + .toList(); + return true; + } else { + print('Der Dialog wurde abgebrochen'); + return true; + } + } catch (e) { + print('Fehler beim Importieren: $e'); + return false; + } + } +} diff --git a/lib/utility/theme.dart b/lib/utility/theme.dart deleted file mode 100644 index 6b4f963..0000000 --- a/lib/utility/theme.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -Color white = CupertinoColors.white; -Color primaryColor = CupertinoColors.systemGreen; -Color backgroundColor = const Color(0xFF101010); -Color backgroundTintColor = CupertinoColors.darkBackgroundGray; - -TextStyle modeTitle = TextStyle( - color: primaryColor, - fontSize: 20, - fontWeight: FontWeight.bold, -); - -const TextStyle modeDescription = TextStyle( - fontSize: 16, -); - -TextStyle createGameTitle = TextStyle( - fontSize: 20, - color: primaryColor, - fontWeight: FontWeight.bold, -); - -TextStyle roundTitle = const TextStyle( - fontSize: 60, - color: CupertinoColors.white, - fontWeight: FontWeight.bold, -); - -TextStyle roundPlayers = const TextStyle( - fontSize: 20, - color: CupertinoColors.white, - fontWeight: FontWeight.bold, -); diff --git a/lib/views/active_game_view.dart b/lib/views/active_game_view.dart index 59d3a1b..85d5a70 100644 --- a/lib/views/active_game_view.dart +++ b/lib/views/active_game_view.dart @@ -1,5 +1,5 @@ import 'package:cabo_counter/data/game_session.dart'; -import 'package:cabo_counter/utility/theme.dart' as theme; +import 'package:cabo_counter/utility/apptheme.dart'; import 'package:cabo_counter/views/round_view.dart'; import 'package:flutter/cupertino.dart'; @@ -28,7 +28,7 @@ class _ActiveGameViewState extends State { padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), child: Text( 'Spieler:innen', - style: theme.createGameTitle, + style: AppTheme.createGameTitle, ), ), ListView.builder( @@ -61,7 +61,7 @@ class _ActiveGameViewState extends State { padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), child: Text( 'Runden', - style: theme.createGameTitle, + style: AppTheme.createGameTitle, ), ), ListView.builder( diff --git a/lib/views/create_game_view.dart b/lib/views/create_game_view.dart index 2ffbe22..e07685a 100644 --- a/lib/views/create_game_view.dart +++ b/lib/views/create_game_view.dart @@ -1,5 +1,7 @@ import 'package:cabo_counter/data/game_session.dart'; -import 'package:cabo_counter/utility/styles.dart'; +import 'package:cabo_counter/utility/apptheme.dart'; +import 'package:cabo_counter/utility/globals.dart'; +import 'package:cabo_counter/utility/local_storage_service.dart'; import 'package:cabo_counter/views/active_game_view.dart'; import 'package:cabo_counter/views/mode_selection_view.dart'; import 'package:flutter/cupertino.dart'; @@ -42,7 +44,7 @@ class _CreateGameState extends State { padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), child: Text( 'Spiel', - style: Styles.createGameTitle, + style: AppTheme.createGameTitle, ), ), Padding( @@ -96,7 +98,7 @@ class _CreateGameState extends State { padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), child: Text( 'Spieler:innen', - style: Styles.createGameTitle, + style: AppTheme.createGameTitle, ), ), Expanded( @@ -273,7 +275,6 @@ class _CreateGameState extends State { ], ), ); - return; } List players = []; @@ -283,8 +284,10 @@ class _CreateGameState extends State { GameSession gameSession = GameSession( gameTitle: _gameTitleTextController.text, players: players, - gameHasPointLimit: selectedMode!, + isPointsLimitEnabled: selectedMode!, ); + Globals.addGameSession(gameSession); + LocalStorageService.saveGameSessions(); Navigator.pushReplacement( context, CupertinoPageRoute( diff --git a/lib/views/information_view.dart b/lib/views/information_view.dart index 667d180..0236427 100644 --- a/lib/views/information_view.dart +++ b/lib/views/information_view.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/utility/local_storage_service.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -14,6 +15,7 @@ class InformationView extends StatelessWidget { @override Widget build(BuildContext context) { return CupertinoPageScaffold( + resizeToAvoidBottomInset: false, navigationBar: const CupertinoNavigationBar( middle: Text('Über'), ), @@ -79,7 +81,52 @@ class InformationView extends StatelessWidget { Uri.parse('https://www.github.com/flixcoo')), icon: const Icon(FontAwesomeIcons.github)), ], - ) + ), + CupertinoButton( + sizeStyle: CupertinoButtonSize.medium, + child: const Text('Spieldaten exportieren'), + onPressed: () async { + final success = await LocalStorageService.exportJsonFile(); + if (!success && context.mounted) { + showCupertinoDialog( + context: context, + builder: (context) => CupertinoAlertDialog( + title: const Text('Fehler'), + content: const Text( + 'Datei konnte nicht exportiert werden.'), + actions: [ + CupertinoDialogAction( + child: const Text('OK'), + onPressed: () => Navigator.pop(context), + ), + ], + ), + ); + } + }, + ), + CupertinoButton( + sizeStyle: CupertinoButtonSize.medium, + child: const Text('Spieldaten importieren'), + onPressed: () async { + final success = + await LocalStorageService.importJsonFile(); + if (!success && context.mounted) { + showCupertinoDialog( + context: context, + builder: (context) => CupertinoAlertDialog( + title: const Text('Fehler'), + content: const Text( + 'Datei konnte nicht importiert werden.'), + actions: [ + CupertinoDialogAction( + child: const Text('OK'), + onPressed: () => Navigator.pop(context), + ), + ], + )); + } + }), ], ), Positioned( diff --git a/lib/views/main_menu_view.dart b/lib/views/main_menu_view.dart index d89a91e..fd3f33f 100644 --- a/lib/views/main_menu_view.dart +++ b/lib/views/main_menu_view.dart @@ -1,4 +1,6 @@ -import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/utility/apptheme.dart'; +import 'package:cabo_counter/utility/globals.dart'; +import 'package:cabo_counter/utility/local_storage_service.dart'; import 'package:cabo_counter/views/active_game_view.dart'; import 'package:cabo_counter/views/create_game_view.dart'; import 'package:cabo_counter/views/information_view.dart'; @@ -14,47 +16,19 @@ class MainMenuView extends StatefulWidget { } class _MainMenuViewState extends State { - final List gameSessionArray = [ - GameSession( - gameTitle: 'Spiel am 27.02.2025', - players: ['Clara', 'Tobias', 'Yannik', 'Lena', 'Lekaia'], - gameHasPointLimit: true), - GameSession( - gameTitle: 'Freundschaftsrunde', - players: ['Felix', 'Jonas', 'Nils'], - gameHasPointLimit: false), - GameSession( - gameTitle: 'Familienabend', - players: ['Mama', 'Papa', 'Lisa'], - gameHasPointLimit: true, - ), - GameSession( - gameTitle: 'Turnier 1. Runde', - players: ['Tim', 'Max', 'Sophie', 'Lena'], - gameHasPointLimit: false), - GameSession( - gameTitle: '2 Namen max length', - players: ['Heinrich', 'Johannes'], - gameHasPointLimit: true), - GameSession( - gameTitle: '3 Namen max length', - players: ['Benjamin', 'Stefanie', 'Wolfgang'], - gameHasPointLimit: false), - GameSession( - gameTitle: '4 Namen max length', - players: ['Leonhard', 'Mathilde', 'Bernhard', 'Gerlinde'], - gameHasPointLimit: true), - GameSession( - gameTitle: '5 Namen max length', - players: ['Hartmuth', 'Elisabet', 'Rosalind', 'Theresia', 'Karoline'], - gameHasPointLimit: false), - ]; + @override + initState() { + super.initState(); + LocalStorageService.loadGameSessions().then((_) { + setState(() {}); + }); + } @override Widget build(BuildContext context) { - gameSessionArray.sort((b, a) => a.createdAt.compareTo(b.createdAt)); - + LocalStorageService.loadGameSessions(); return CupertinoPageScaffold( + resizeToAvoidBottomInset: false, navigationBar: CupertinoNavigationBar( leading: IconButton( onPressed: () { @@ -83,49 +57,74 @@ class _MainMenuViewState extends State { ), child: CupertinoPageScaffold( child: SafeArea( - child: ListView.builder( - itemCount: gameSessionArray.length, - itemBuilder: (context, index) { - final session = gameSessionArray[index]; - return 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.gameHasPointLimit)}', - 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), - ], + child: 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: AppTheme.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), + ), ), - onTap: () async { - //ignore: unused_local_variable - final val = await Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ActiveGameView( - gameSession: gameSessionArray[index]), - ), - ); - setState(() {}); - }, - )); - }, - ), + ], + ) + : ListView.builder( + itemCount: Globals.gameList.length, + itemBuilder: (context, index) { + final session = Globals.gameList[index]; + return 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(() {}); + }, + )); + }), ), ), ); diff --git a/lib/views/mode_selection_view.dart b/lib/views/mode_selection_view.dart index 13a4b99..7b55b30 100644 --- a/lib/views/mode_selection_view.dart +++ b/lib/views/mode_selection_view.dart @@ -1,5 +1,4 @@ -import 'package:cabo_counter/utility/styles.dart'; -import 'package:cabo_counter/utility/theme.dart' as theme; +import 'package:cabo_counter/utility/apptheme.dart'; import 'package:flutter/cupertino.dart'; class ModeSelectionMenu extends StatelessWidget { @@ -16,10 +15,10 @@ class ModeSelectionMenu extends StatelessWidget { Padding( padding: const EdgeInsets.fromLTRB(0, 16, 0, 0), child: CupertinoListTile( - title: Text('101 Punkte', style: Styles.modeTitle), + title: Text('101 Punkte', style: AppTheme.modeTitle), subtitle: const Text( 'Es wird solange gespielt, bis einer Spieler mehr als 100 Punkte erreicht', - style: Styles.modeDescription, + style: AppTheme.modeDescription, maxLines: 3, ), onTap: () { @@ -30,11 +29,11 @@ class ModeSelectionMenu extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: CupertinoListTile( - title: Text('Unbegrenzt', style: theme.modeTitle), + title: Text('Unbegrenzt', style: AppTheme.modeTitle), subtitle: const Text( 'Dem Spiel sind keine Grenzen gesetzt. Es wird so lange ' 'gespielt, bis Ihr keine Lust mehr habt.', - style: Styles.modeDescription, + style: AppTheme.modeDescription, maxLines: 3, ), onTap: () { diff --git a/lib/views/round_view.dart b/lib/views/round_view.dart index 9520aa5..9a5a035 100644 --- a/lib/views/round_view.dart +++ b/lib/views/round_view.dart @@ -1,5 +1,6 @@ import 'package:cabo_counter/data/game_session.dart'; -import 'package:cabo_counter/utility/theme.dart' as theme; +import 'package:cabo_counter/utility/apptheme.dart'; +import 'package:cabo_counter/utility/local_storage_service.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; @@ -71,7 +72,10 @@ class _RoundViewState extends State { previousPageTitle: 'Übersicht', leading: CupertinoButton( padding: EdgeInsets.zero, - onPressed: () => Navigator.pop(context, widget.gameSession), + onPressed: () => { + LocalStorageService.saveGameSessions(), + Navigator.pop(context, widget.gameSession) + }, child: const Text('Abbrechen'), ), ), @@ -86,7 +90,7 @@ class _RoundViewState extends State { children: [ const SizedBox(height: 40), Text('Runde ${widget.roundNumber}', - style: theme.roundTitle), + style: AppTheme.roundTitle), const SizedBox(height: 10), const Text( 'Wer hat CABO gesagt?', @@ -101,8 +105,8 @@ class _RoundViewState extends State { child: SizedBox( height: 40, child: CupertinoSegmentedControl( - unselectedColor: theme.backgroundTintColor, - selectedColor: theme.primaryColor, + unselectedColor: AppTheme.backgroundTintColor, + selectedColor: AppTheme.primaryColor, groupValue: _caboPlayerIndex, children: Map.fromEntries(widget.gameSession.players .asMap() @@ -267,7 +271,7 @@ class _RoundViewState extends State { return Container( height: 80, padding: const EdgeInsets.only(bottom: 20), - color: theme.backgroundTintColor, + color: AppTheme.backgroundTintColor, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ @@ -275,6 +279,7 @@ class _RoundViewState extends State { onPressed: _areRoundInputsValid() ? () { _finishRound(); + LocalStorageService.saveGameSessions(); Navigator.pop(context, widget.gameSession); } : null, @@ -284,6 +289,7 @@ class _RoundViewState extends State { onPressed: _areRoundInputsValid() ? () { _finishRound(); + LocalStorageService.saveGameSessions(); if (widget.gameSession.isGameFinished == true) { Navigator.pop(context, widget.gameSession); } else { diff --git a/pubspec.yaml b/pubspec.yaml index 326e7ee..a20057a 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.3+65 +version: 0.1.5+110 environment: sdk: ^3.5.4 @@ -11,15 +11,18 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.8 - font_awesome_flutter: ^10.8.0 - url_launcher: any - package_info_plus: any + file_picker: ^10.1.2 + file_saver: ^0.2.6 flutter_keyboard_visibility: ^6.0.0 + font_awesome_flutter: ^10.8.0 + package_info_plus: any + path_provider: ^2.1.1 + typed_data: ^1.3.2 + url_launcher: any dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^5.0.0 test: ^1.25.15