Fixed state update bug

This commit is contained in:
2025-06-08 19:11:57 +02:00
parent 1f03c0fbce
commit 845528e362
8 changed files with 178 additions and 156 deletions

View File

@@ -8,8 +8,8 @@ import 'package:flutter/cupertino.dart';
Future<void> main() async { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await ConfigService.initConfig(); await ConfigService.initConfig();
Globals.pointLimit = await ConfigService.getPointLimit(); globals.pointLimit = await ConfigService.getPointLimit();
Globals.caboPenalty = await ConfigService.getCaboPenalty(); globals.caboPenalty = await ConfigService.getCaboPenalty();
runApp(const App()); runApp(const App());
} }

View File

@@ -48,8 +48,8 @@ class ConfigService {
/// Resets the configuration to default values. /// Resets the configuration to default values.
static Future<void> resetConfig() async { static Future<void> resetConfig() async {
Globals.pointLimit = _defaultPointLimit; globals.pointLimit = _defaultPointLimit;
Globals.caboPenalty = _defaultCaboPenalty; globals.caboPenalty = _defaultCaboPenalty;
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

@@ -19,7 +19,7 @@ class LocalStorageService {
/// Writes the game session list to a JSON file and returns it as string. /// Writes the game session list to a JSON file and returns it as string.
static String getJsonFile() { static String getJsonFile() {
final jsonFile = final jsonFile =
Globals.gameList.map((session) => session.toJson()).toList(); globals.gameList.map((session) => session.toJson()).toList();
return json.encode(jsonFile); return json.encode(jsonFile);
} }
@@ -63,14 +63,14 @@ class LocalStorageService {
if (!await validateJsonSchema(jsonString)) { if (!await validateJsonSchema(jsonString)) {
logger.w('Die Datei konnte nicht validiert werden'); logger.w('Die Datei konnte nicht validiert werden');
Globals.gameList = []; globals.gameList = [];
return false; return false;
} }
logger.d('Die gefundene Datei hat Inhalt'); logger.d('Die gefundene Datei hat Inhalt');
logger.d('Die gefundene Datei wurde erfolgreich validiert'); logger.d('Die gefundene Datei wurde erfolgreich validiert');
final jsonList = json.decode(jsonString) as List<dynamic>; final jsonList = json.decode(jsonString) as List<dynamic>;
Globals.gameList = jsonList globals.gameList = jsonList
.map((jsonItem) => .map((jsonItem) =>
GameSession.fromJson(jsonItem as Map<String, dynamic>)) GameSession.fromJson(jsonItem as Map<String, dynamic>))
.toList(); .toList();
@@ -80,7 +80,7 @@ class LocalStorageService {
} catch (e) { } catch (e) {
logger.e('Fehler beim Laden der Spieldaten:\n$e', logger.e('Fehler beim Laden der Spieldaten:\n$e',
error: 'JSON nicht geladen'); error: 'JSON nicht geladen');
Globals.gameList = []; globals.gameList = [];
return false; return false;
} }
} }
@@ -125,7 +125,7 @@ class LocalStorageService {
return false; return false;
} }
final jsonData = json.decode(jsonString) as List<dynamic>; final jsonData = json.decode(jsonString) as List<dynamic>;
Globals.gameList = jsonData globals.gameList = jsonData
.map((jsonItem) => .map((jsonItem) =>
GameSession.fromJson(jsonItem as Map<String, dynamic>)) GameSession.fromJson(jsonItem as Map<String, dynamic>))
.toList(); .toList();
@@ -172,7 +172,7 @@ class LocalStorageService {
static Future<bool> deleteAllGames() async { static Future<bool> deleteAllGames() async {
try { try {
Globals.gameList.clear(); globals.gameList.clear();
await saveGameSessions(); await saveGameSessions();
logger.i('Alle Runden wurden erfolgreich gelöscht.'); logger.i('Alle Runden wurden erfolgreich gelöscht.');
return true; return true;

View File

@@ -1,17 +1,25 @@
import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/services/local_storage_service.dart';
import 'package:flutter/foundation.dart';
class Globals { class Globals extends ChangeNotifier {
/// The [gameList] contains all active game sessions. List<GameSession> gameList = [];
static List<GameSession> gameList = []; int pointLimit = 100;
int caboPenalty = 5;
String appDevPhase = 'Alpha';
static void addGameSession(GameSession session) { void addGameSession(GameSession session) {
gameList.add(session); gameList.add(session);
gameList.sort((a, b) => b.createdAt.compareTo(a.createdAt)); gameList.sort((a, b) => b.createdAt.compareTo(a.createdAt));
notifyListeners(); // Wichtig!
LocalStorageService.saveGameSessions();
} }
static int pointLimit = 100; void removeGameSession(int index) {
gameList.removeAt(index);
static int caboPenalty = 5; notifyListeners(); // Wichtig!
LocalStorageService.saveGameSessions();
static String appDevPhase = 'Alpha';
} }
}
final globals = Globals(); // Globale Instanz

View File

@@ -81,7 +81,7 @@ class _CreateGameState extends State<CreateGame> {
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (context) => ModeSelectionMenu( builder: (context) => ModeSelectionMenu(
pointLimit: Globals.pointLimit, pointLimit: globals.pointLimit,
), ),
), ),
); );
@@ -285,13 +285,11 @@ class _CreateGameState extends State<CreateGame> {
createdAt: DateTime.now(), createdAt: DateTime.now(),
gameTitle: _gameTitleTextController.text, gameTitle: _gameTitleTextController.text,
players: players, players: players,
pointLimit: Globals.pointLimit, pointLimit: globals.pointLimit,
caboPenalty: Globals.caboPenalty, caboPenalty: globals.caboPenalty,
isPointsLimitEnabled: selectedMode!, isPointsLimitEnabled: selectedMode!,
); );
setState(() { globals.addGameSession(gameSession);
Globals.addGameSession(gameSession);
});
LocalStorageService.saveGameSessions(); LocalStorageService.saveGameSessions();
if (context.mounted) { if (context.mounted) {
Navigator.pushReplacement( Navigator.pushReplacement(

View File

@@ -26,6 +26,11 @@ class _MainMenuViewState extends State<MainMenuView> {
_isLoading = false; _isLoading = false;
}); });
}); });
globals.addListener(_updateView);
}
void _updateView() {
if (mounted) setState(() {});
} }
@override @override
@@ -33,6 +38,9 @@ class _MainMenuViewState extends State<MainMenuView> {
print('MainMenuView build'); print('MainMenuView build');
LocalStorageService.loadGameSessions(); LocalStorageService.loadGameSessions();
return ListenableBuilder(
listenable: globals,
builder: (context, _) {
return CupertinoPageScaffold( return CupertinoPageScaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
navigationBar: CupertinoNavigationBar( navigationBar: CupertinoNavigationBar(
@@ -62,7 +70,7 @@ class _MainMenuViewState extends State<MainMenuView> {
child: SafeArea( child: SafeArea(
child: _isLoading child: _isLoading
? const Center(child: CupertinoActivityIndicator()) ? const Center(child: CupertinoActivityIndicator())
: Globals.gameList.isEmpty : globals.gameList.isEmpty
? Column( ? Column(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.center, // Oben ausrichten MainAxisAlignment.center, // Oben ausrichten
@@ -89,9 +97,9 @@ class _MainMenuViewState extends State<MainMenuView> {
], ],
) )
: ListView.builder( : ListView.builder(
itemCount: Globals.gameList.length, itemCount: globals.gameList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final session = Globals.gameList[index]; final session = globals.gameList[index];
return Dismissible( return Dismissible(
key: Key(session.gameTitle), key: Key(session.gameTitle),
background: Container( background: Container(
@@ -106,7 +114,7 @@ class _MainMenuViewState extends State<MainMenuView> {
direction: DismissDirection.startToEnd, direction: DismissDirection.startToEnd,
confirmDismiss: (direction) async { confirmDismiss: (direction) async {
final String gameTitle = final String gameTitle =
Globals.gameList[index].gameTitle; globals.gameList[index].gameTitle;
return await _showDeleteGamePopup(gameTitle); return await _showDeleteGamePopup(gameTitle);
}, },
onDismissed: (direction) { onDismissed: (direction) {
@@ -116,17 +124,22 @@ class _MainMenuViewState extends State<MainMenuView> {
DismissDirection.startToEnd: 0.6 DismissDirection.startToEnd: 0.6
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0), padding: const EdgeInsets.symmetric(
vertical: 10.0),
child: CupertinoListTile( child: CupertinoListTile(
backgroundColorActivated:
CustomTheme.backgroundColor,
title: Text(session.gameTitle), title: Text(session.gameTitle),
subtitle: session.isGameFinished == true subtitle: session.isGameFinished == true
? Text( ? Text(
'\u{1F947} ${session.winner}', '\u{1F947} ${session.winner}',
style: const TextStyle(fontSize: 14), style:
const TextStyle(fontSize: 14),
) )
: Text( : Text(
'Modus: ${_translateGameMode(session.isPointsLimitEnabled)}', 'Modus: ${_translateGameMode(session.isPointsLimitEnabled)}',
style: const TextStyle(fontSize: 14), style:
const TextStyle(fontSize: 14),
), ),
trailing: Row( trailing: Row(
children: [ children: [
@@ -137,7 +150,8 @@ class _MainMenuViewState extends State<MainMenuView> {
const SizedBox(width: 15), const SizedBox(width: 15),
Text('${session.players.length}'), Text('${session.players.length}'),
const SizedBox(width: 3), const SizedBox(width: 3),
const Icon(CupertinoIcons.person_2_fill), const Icon(
CupertinoIcons.person_2_fill),
], ],
), ),
onTap: () async { onTap: () async {
@@ -146,7 +160,8 @@ class _MainMenuViewState extends State<MainMenuView> {
context, context,
CupertinoPageRoute( CupertinoPageRoute(
builder: (context) => ActiveGameView( builder: (context) => ActiveGameView(
gameSession: Globals.gameList[index]), gameSession:
globals.gameList[index]),
), ),
); );
setState(() {}); setState(() {});
@@ -159,6 +174,7 @@ class _MainMenuViewState extends State<MainMenuView> {
), ),
), ),
); );
});
} }
/// Translates the game mode boolean into the corresponding String. /// Translates the game mode boolean into the corresponding String.
@@ -204,7 +220,7 @@ class _MainMenuViewState extends State<MainMenuView> {
/// This function takes an [index] as parameter and removes the game session at /// This function takes an [index] as parameter and removes the game session at
/// that index from the global game list, /// that index from the global game list,
void _deleteSpecificGame(int index) { void _deleteSpecificGame(int index) {
Globals.gameList.removeAt(index); globals.gameList.removeAt(index);
LocalStorageService.saveGameSessions(); LocalStorageService.saveGameSessions();
} }
} }

View File

@@ -50,14 +50,14 @@ class _SettingsViewState extends State<SettingsView> {
subtitle: const Text('... für falsches Cabo sagen'), subtitle: const Text('... für falsches Cabo sagen'),
trailing: Stepper( trailing: Stepper(
key: _stepperKey1, key: _stepperKey1,
initialValue: Globals.caboPenalty, initialValue: globals.caboPenalty,
minValue: 0, minValue: 0,
maxValue: 50, maxValue: 50,
step: 1, step: 1,
onChanged: (newCaboPenalty) { onChanged: (newCaboPenalty) {
setState(() { setState(() {
ConfigService.setCaboPenalty(newCaboPenalty); ConfigService.setCaboPenalty(newCaboPenalty);
Globals.caboPenalty = newCaboPenalty; globals.caboPenalty = newCaboPenalty;
}); });
}, },
), ),
@@ -70,14 +70,14 @@ class _SettingsViewState extends State<SettingsView> {
subtitle: const Text('... hier ist Schluss'), subtitle: const Text('... hier ist Schluss'),
trailing: Stepper( trailing: Stepper(
key: _stepperKey2, key: _stepperKey2,
initialValue: Globals.pointLimit, initialValue: globals.pointLimit,
minValue: 30, minValue: 30,
maxValue: 1000, maxValue: 1000,
step: 10, step: 10,
onChanged: (newPointLimit) { onChanged: (newPointLimit) {
setState(() { setState(() {
ConfigService.setPointLimit(newPointLimit); ConfigService.setPointLimit(newPointLimit);
Globals.pointLimit = newPointLimit; globals.pointLimit = newPointLimit;
}); });
}, },
), ),
@@ -201,7 +201,7 @@ class _SettingsViewState extends State<SettingsView> {
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
return Text( return Text(
'${Globals.appDevPhase} ${snapshot.data!.version} ' '${globals.appDevPhase} ${snapshot.data!.version} '
'(Build ${snapshot.data!.buildNumber})', '(Build ${snapshot.data!.buildNumber})',
textAlign: TextAlign.center, textAlign: TextAlign.center,
); );

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.1.6+146 version: 0.1.6+148
environment: environment:
sdk: ^3.5.4 sdk: ^3.5.4