Fixed bug for games showing round number 1 in main menu after creating

This commit is contained in:
2025-06-11 15:27:34 +02:00
parent c7c48d6022
commit 0374678b91
4 changed files with 184 additions and 156 deletions

View File

@@ -11,6 +11,9 @@ class GameManager extends ChangeNotifier {
/// It also saves the updated game sessions to local storage. /// It also saves the updated game sessions to local storage.
/// Returns the index of the newly added session in the sorted list. /// Returns the index of the newly added session in the sorted list.
Future<int> addGameSession(GameSession session) async { Future<int> addGameSession(GameSession session) async {
session.addListener(() {
notifyListeners(); // Propagate session changes
});
gameList.add(session); gameList.add(session);
print( print(
'[game_manager.dart] Added game session: ${session.gameTitle} at ${session.createdAt}'); '[game_manager.dart] Added game session: ${session.gameTitle} at ${session.createdAt}');
@@ -28,6 +31,7 @@ class GameManager extends ChangeNotifier {
/// sorts the list in descending order based on the creation date, and notifies listeners of the change. /// 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. /// It also saves the updated game sessions to local storage.
void removeGameSession(int index) { void removeGameSession(int index) {
gameList[index].removeListener(notifyListeners);
gameList.removeAt(index); gameList.removeAt(index);
notifyListeners(); notifyListeners();
LocalStorageService.saveGameSessions(); LocalStorageService.saveGameSessions();

View File

@@ -11,7 +11,7 @@ import 'package:flutter/cupertino.dart';
/// [roundNumber] is the current round number. /// [roundNumber] is the current round number.
/// [isGameFinished] is a boolean indicating if the game has ended yet. /// [isGameFinished] is a boolean indicating if the game has ended yet.
/// [winner] is the name of the player who won the game. /// [winner] is the name of the player who won the game.
class GameSession { class GameSession extends ChangeNotifier {
final DateTime createdAt; final DateTime createdAt;
final String gameTitle; final String gameTitle;
final List<String> players; final List<String> players;
@@ -222,6 +222,7 @@ class GameSession {
} else { } else {
roundList[roundNum - 1] = newRound; roundList[roundNum - 1] = newRound;
} }
notifyListeners();
} }
/// This method updates the points of each player after a round. /// This method updates the points of each player after a round.
@@ -248,6 +249,7 @@ class GameSession {
} }
} }
} }
notifyListeners();
} }
@visibleForTesting @visibleForTesting
@@ -262,6 +264,7 @@ class GameSession {
playerScores[i] += roundList[j].scoreUpdates[i]; playerScores[i] += roundList[j].scoreUpdates[i];
} }
} }
notifyListeners();
} }
/// Checks if a player has reached 100 points in the current round. /// Checks if a player has reached 100 points in the current round.
@@ -291,10 +294,14 @@ class GameSession {
} }
} }
winner = lowestPlayer; winner = lowestPlayer;
notifyListeners();
} }
/// Increases the round number by 1. /// Increases the round number by 1.
void increaseRound() { void increaseRound() {
roundNumber++; roundNumber++;
print('roundNumber erhöht: $roundNumber — Hash: ${identityHashCode(this)}');
notifyListeners();
} }
} }

View File

@@ -15,92 +15,98 @@ class ActiveGameView extends StatefulWidget {
class _ActiveGameViewState extends State<ActiveGameView> { class _ActiveGameViewState extends State<ActiveGameView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<int> sortedPlayerIndices = _getSortedPlayerIndices(); return ListenableBuilder(
return CupertinoPageScaffold( listenable: widget.gameSession,
navigationBar: CupertinoNavigationBar( builder: (context, _) {
middle: Text(widget.gameSession.gameTitle), List<int> sortedPlayerIndices = _getSortedPlayerIndices();
), return CupertinoPageScaffold(
child: SafeArea( navigationBar: CupertinoNavigationBar(
child: Column( middle: Text(widget.gameSession.gameTitle),
crossAxisAlignment: CrossAxisAlignment.start, ),
children: [ child: SafeArea(
Padding( child: Column(
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), crossAxisAlignment: CrossAxisAlignment.start,
child: Text( children: [
'Spieler:innen', Padding(
style: CustomTheme.rowTitle, padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Text(
'Spieler:innen',
style: CustomTheme.rowTitle,
),
),
ListView.builder(
shrinkWrap: true,
itemCount: widget.gameSession.players.length,
itemBuilder: (BuildContext context, int index) {
int playerIndex = sortedPlayerIndices[index];
return CupertinoListTile(
title: Row(
children: [
_getPlacementPrefix(index),
const SizedBox(width: 5),
Text(
widget.gameSession.players[playerIndex],
style:
const TextStyle(fontWeight: FontWeight.bold),
),
],
),
trailing: Row(
children: [
const SizedBox(width: 5),
Text(
'${widget.gameSession.playerScores[playerIndex]} '
'Punkte')
],
),
);
},
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Text(
'Runden',
style: CustomTheme.rowTitle,
),
),
ListView.builder(
shrinkWrap: true,
itemCount: widget.gameSession.roundNumber,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(1),
child: CupertinoListTile(
title: Text(
'Runde ${index + 1}',
),
trailing: index + 1 !=
widget.gameSession.roundNumber ||
widget.gameSession.isGameFinished == true
? (const Text('\u{2705}',
style: TextStyle(fontSize: 22)))
: const Text('\u{23F3}',
style: TextStyle(fontSize: 22)),
onTap: () async {
// ignore: unused_local_variable
final val = await Navigator.of(context,
rootNavigator: true)
.push(
CupertinoPageRoute(
fullscreenDialog: true,
builder: (context) => RoundView(
gameSession: widget.gameSession,
roundNumber: index + 1),
),
);
},
));
},
),
],
), ),
), ),
ListView.builder( );
shrinkWrap: true, });
itemCount: widget.gameSession.players.length,
itemBuilder: (BuildContext context, int index) {
int playerIndex = sortedPlayerIndices[index];
return CupertinoListTile(
title: Row(
children: [
_getPlacementPrefix(index),
const SizedBox(width: 5),
Text(
widget.gameSession.players[playerIndex],
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
trailing: Row(
children: [
const SizedBox(width: 5),
Text('${widget.gameSession.playerScores[playerIndex]} '
'Punkte')
],
),
);
},
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Text(
'Runden',
style: CustomTheme.rowTitle,
),
),
ListView.builder(
shrinkWrap: true,
itemCount: widget.gameSession.roundNumber,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(1),
child: CupertinoListTile(
title: Text(
'Runde ${index + 1}',
),
trailing: index + 1 != widget.gameSession.roundNumber ||
widget.gameSession.isGameFinished == true
? (const Text('\u{2705}',
style: TextStyle(fontSize: 22)))
: const Text('\u{23F3}',
style: TextStyle(fontSize: 22)),
onTap: () async {
// ignore: unused_local_variable
final val =
await Navigator.of(context, rootNavigator: true)
.push(
CupertinoPageRoute(
fullscreenDialog: true,
builder: (context) => RoundView(
gameSession: widget.gameSession,
roundNumber: index + 1),
),
);
setState(() {});
},
));
},
),
],
),
),
);
} }
/// Returns a list of player indices sorted by their scores in /// Returns a list of player indices sorted by their scores in

View File

@@ -35,9 +35,6 @@ class _MainMenuViewState extends State<MainMenuView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print('MainMenuView build');
LocalStorageService.loadGameSessions();
return ListenableBuilder( return ListenableBuilder(
listenable: gameManager, listenable: gameManager,
builder: (context, _) { builder: (context, _) {
@@ -100,75 +97,83 @@ class _MainMenuViewState extends State<MainMenuView> {
itemCount: gameManager.gameList.length, itemCount: gameManager.gameList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final session = gameManager.gameList[index]; final session = gameManager.gameList[index];
return Dismissible( return ListenableBuilder(
key: Key(session.gameTitle), listenable: session,
background: Container( builder: (context, _) {
color: CupertinoColors.destructiveRed, return Dismissible(
alignment: Alignment.centerLeft, key: Key(session.gameTitle),
padding: const EdgeInsets.only(left: 20.0), background: Container(
child: const Icon( color: CupertinoColors.destructiveRed,
CupertinoIcons.delete, alignment: Alignment.centerLeft,
color: CupertinoColors.white, padding:
), const EdgeInsets.only(left: 20.0),
), child: const Icon(
direction: DismissDirection.startToEnd, CupertinoIcons.delete,
confirmDismiss: (direction) async { color: CupertinoColors.white,
final String gameTitle =
gameManager.gameList[index].gameTitle;
return await _showDeleteGamePopup(gameTitle);
},
onDismissed: (direction) {
gameManager.removeGameSession(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:
gameManager.gameList[index]),
), ),
); ),
setState(() {}); direction: DismissDirection.startToEnd,
}, confirmDismiss: (direction) async {
), final String gameTitle = gameManager
), .gameList[index].gameTitle;
); return await _showDeleteGamePopup(
gameTitle);
},
onDismissed: (direction) {
gameManager.removeGameSession(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: gameManager
.gameList[index]),
),
);
setState(() {});
},
),
),
);
});
}, },
), ),
), ),
@@ -215,4 +220,10 @@ class _MainMenuViewState extends State<MainMenuView> {
false; false;
return shouldDelete; return shouldDelete;
} }
@override
void dispose() {
gameManager.removeListener(_updateView);
super.dispose();
}
} }