Merge pull request #17 from flixcoo/feature/13-saving-and-retrieving-game-data-as-json

feature/13 saving and retrieving game data as json
This commit is contained in:
2025-05-02 11:49:47 +02:00
committed by GitHub
14 changed files with 424 additions and 158 deletions

View File

@@ -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<String> players;
late List<int> playerScores;
List<Round> 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<String, dynamic> 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<String, dynamic> json)
: gameTitle = json['gameTitle'],
isPointsLimitEnabled = json['gameHasPointLimit'],
players = List<String>.from(json['players']),
playerScores = List<int>.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++) {

View File

@@ -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<String, dynamic> toJson() => {
'roundNum': roundNum,
'scores': scores,
'scoreUpdates': scoreUpdates,
'kamikazePlayerIndex': kamikazePlayerIndex,
};
/// Creates a Round object from a JSON map.
Round.fromJson(Map<String, dynamic> json)
: roundNum = json['roundNum'],
scores = List<int>.from(json['scores']),
scoreUpdates = List<int>.from(json['scoreUpdates']),
kamikazePlayerIndex = json['kamikazePlayerIndex'];
}

View File

@@ -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<StatefulWidget> createState() => _AppState();
}
class _AppState extends State<App> 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,

View File

@@ -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,
);
}

View File

@@ -1,5 +1,11 @@
import 'package:cabo_counter/data/game_session.dart';
class Globals {
static Map<int, GameSession> gamesMap = <int, GameSession>{};
/// The [gameList] contains all active game sessions.
static List<GameSession> gameList = [];
static void addGameSession(GameSession session) {
gameList.add(session);
gameList.sort((a, b) => b.createdAt.compareTo(a.createdAt));
}
}

View File

@@ -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<File> _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<void> 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<void> 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<dynamic>;
Globals.gameList = jsonList
.map((jsonItem) =>
GameSession.fromJson(jsonItem as Map<String, dynamic>))
.toList()
.cast<GameSession>();
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<bool> 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<bool> 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<dynamic>;
print('JSON Inhalt: $jsonList');
Globals.gameList = jsonList
.map((jsonItem) =>
GameSession.fromJson(jsonItem as Map<String, dynamic>))
.toList();
return true;
} else {
print('Der Dialog wurde abgebrochen');
return true;
}
} catch (e) {
print('Fehler beim Importieren: $e');
return false;
}
}
}

View File

@@ -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,
);

View File

@@ -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<ActiveGameView> {
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<ActiveGameView> {
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Text(
'Runden',
style: theme.createGameTitle,
style: AppTheme.createGameTitle,
),
),
ListView.builder(

View File

@@ -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<CreateGame> {
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<CreateGame> {
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<CreateGame> {
],
),
);
return;
}
List<String> players = [];
@@ -283,8 +284,10 @@ class _CreateGameState extends State<CreateGame> {
GameSession gameSession = GameSession(
gameTitle: _gameTitleTextController.text,
players: players,
gameHasPointLimit: selectedMode!,
isPointsLimitEnabled: selectedMode!,
);
Globals.addGameSession(gameSession);
LocalStorageService.saveGameSessions();
Navigator.pushReplacement(
context,
CupertinoPageRoute(

View File

@@ -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(

View File

@@ -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<MainMenuView> {
final List<GameSession> 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<MainMenuView> {
),
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(() {});
},
));
}),
),
),
);

View File

@@ -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: () {

View File

@@ -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<RoundView> {
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<RoundView> {
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<RoundView> {
child: SizedBox(
height: 40,
child: CupertinoSegmentedControl<int>(
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<RoundView> {
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<RoundView> {
onPressed: _areRoundInputsValid()
? () {
_finishRound();
LocalStorageService.saveGameSessions();
Navigator.pop(context, widget.gameSession);
}
: null,
@@ -284,6 +289,7 @@ class _RoundViewState extends State<RoundView> {
onPressed: _areRoundInputsValid()
? () {
_finishRound();
LocalStorageService.saveGameSessions();
if (widget.gameSession.isGameFinished == true) {
Navigator.pop(context, widget.gameSession);
} else {

View File

@@ -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