From bbb7914fc5360bd46a6fd0df4710bc53456e9af6 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 1 Jan 2026 16:39:57 +0100 Subject: [PATCH 01/12] Updated sorting logic & added comments --- .../widgets/player_selection.dart | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index eb70ae0..5f77e1f 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -149,18 +149,24 @@ class _PlayerSelectionState extends State { onIconTap: () { setState(() { // Removes the player from the selection and notifies the parent. + selectedPlayers.remove(player); + widget.onChanged([...selectedPlayers]); + + // Get the current search query final currentSearch = _searchBarController .text .toLowerCase(); - selectedPlayers.remove(player); - widget.onChanged([...selectedPlayers]); + // If the player matches the current search query (or search is empty), - // they are added back to the suggestions and the list is re-sorted. + // they are added back to the `suggestedPlayers` and the list is re-sorted. if (currentSearch.isEmpty || player.name.toLowerCase().contains( currentSearch, )) { suggestedPlayers.add(player); + suggestedPlayers.sort( + (a, b) => a.name.compareTo(b.name), + ); } }); }, @@ -200,11 +206,15 @@ class _PlayerSelectionState extends State { text: suggestedPlayers[index].name, onPressed: () { setState(() { + // If the player is not already selected if (!selectedPlayers.contains( suggestedPlayers[index], )) { - selectedPlayers.add(suggestedPlayers[index]); + // Add to player to the front of the selectedPlayers + selectedPlayers.insert(0, suggestedPlayers[index]); + // Notify the parent widget of the change widget.onChanged([...selectedPlayers]); + // Remove the player from the suggestedPlayers suggestedPlayers.remove(suggestedPlayers[index]); } }); @@ -221,7 +231,7 @@ class _PlayerSelectionState extends State { } /// Adds a new player to the database from the search bar input. - /// Shows a snackbar indicating success or failure. + /// Shows a snackbar indicating success xfor failure. /// [context] - BuildContext to show the snackbar. void addNewPlayerFromSearch({required BuildContext context}) async { String playerName = _searchBarController.text.trim(); From 15d09f381aaa60798626871fbccb66925e14ff05 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 1 Jan 2026 17:47:15 +0100 Subject: [PATCH 02/12] New created players will be added now at the front too --- lib/presentation/widgets/player_selection.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 5f77e1f..d497fcd 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -239,7 +239,7 @@ class _PlayerSelectionState extends State { bool success = await db.playerDao.addPlayer(player: createdPlayer); if (!context.mounted) return; if (success) { - selectedPlayers.add(createdPlayer); + selectedPlayers.insert(0, createdPlayer); widget.onChanged([...selectedPlayers]); allPlayers.add(createdPlayer); setState(() { From 5ee0d59377a70b31bfe69eddd25277fed3f9678b Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 1 Jan 2026 19:13:50 +0100 Subject: [PATCH 03/12] implement optional match name with game name as default --- .../create_match/create_match_view.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index f3b4d79..f2f86be 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -31,6 +31,8 @@ class _CreateMatchViewState extends State { /// Controller for the match name input field final TextEditingController _matchNameController = TextEditingController(); + String hintText = "Match Name"; + /// List of all groups from the database List groupsList = []; @@ -132,7 +134,7 @@ class _CreateMatchViewState extends State { margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 5), child: TextInputField( controller: _matchNameController, - hintText: 'Match name', + hintText: hintText, ), ), ChooseTile( @@ -151,11 +153,13 @@ class _CreateMatchViewState extends State { ); setState(() { if (selectedGameIndex != -1) { + hintText = games[selectedGameIndex].$1; selectedRuleset = games[selectedGameIndex].$3; selectedRulesetIndex = rulesets.indexWhere( (r) => r.$1 == selectedRuleset, ); } else { + hintText = "Match Name"; selectedRuleset = null; } }); @@ -228,7 +232,9 @@ class _CreateMatchViewState extends State { onPressed: _enableCreateGameButton() ? () async { Match match = Match( - name: _matchNameController.text.trim(), + name: _matchNameController.text.isEmpty + ? hintText.trim() + : _matchNameController.text.trim(), createdAt: DateTime.now(), group: selectedGroup, players: selectedPlayers, @@ -258,9 +264,8 @@ class _CreateMatchViewState extends State { /// Determines whether the "Create Game" button should be enabled based on /// the current state of the input fields. bool _enableCreateGameButton() { - return _matchNameController.text.isNotEmpty && - (selectedGroup != null || - (selectedPlayers != null && selectedPlayers!.length > 1)) && - selectedRuleset != null; + return selectedGroup != null || + (selectedPlayers != null && selectedPlayers!.length > 1) && + selectedRuleset != null; } } From db30b0fd5ab004fbacb1a7e72dfecdbb6eda66eb Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 1 Jan 2026 19:15:57 +0100 Subject: [PATCH 04/12] removed double quotes --- .../main_menu/match_view/create_match/create_match_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index f2f86be..05da60d 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -31,7 +31,7 @@ class _CreateMatchViewState extends State { /// Controller for the match name input field final TextEditingController _matchNameController = TextEditingController(); - String hintText = "Match Name"; + String hintText = 'Match Name'; /// List of all groups from the database List groupsList = []; @@ -159,7 +159,7 @@ class _CreateMatchViewState extends State { (r) => r.$1 == selectedRuleset, ); } else { - hintText = "Match Name"; + hintText = 'Match Name'; selectedRuleset = null; } }); From 55a22b292f3cda7ddb1183dff4c5a758835e3513 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 1 Jan 2026 21:40:18 +0100 Subject: [PATCH 05/12] changed bottom padding --- lib/presentation/views/main_menu/match_view/match_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index e7d29c0..96d40fb 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -71,7 +71,7 @@ class _MatchViewState extends State { itemBuilder: (BuildContext context, int index) { if (index == matches.length) { return SizedBox( - height: MediaQuery.paddingOf(context).bottom - 80, + height: MediaQuery.paddingOf(context).bottom - 20, ); } return GameHistoryTile( From 6899bb0c3c48f61d8f4f78d541674e8744de8d95 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 1 Jan 2026 21:47:13 +0100 Subject: [PATCH 06/12] add comment --- .../main_menu/match_view/create_match/create_match_view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index 05da60d..6ef53ba 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -31,6 +31,7 @@ class _CreateMatchViewState extends State { /// Controller for the match name input field final TextEditingController _matchNameController = TextEditingController(); + /// Hint text for the match name input field String hintText = 'Match Name'; /// List of all groups from the database From 1d4fdae84b0a3410ee5df6470727878835a34337 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 1 Jan 2026 22:40:26 +0100 Subject: [PATCH 07/12] Hotfix: Missing game renames --- lib/presentation/views/main_menu/home_view.dart | 12 ++++++------ .../views/main_menu/match_view/match_view.dart | 10 +++++----- .../{game_tile.dart => match_summary_tile.dart} | 8 ++++---- .../{game_history_tile.dart => match_tile.dart} | 8 ++++---- 4 files changed, 19 insertions(+), 19 deletions(-) rename lib/presentation/widgets/tiles/{game_tile.dart => match_summary_tile.dart} (92%) rename lib/presentation/widgets/tiles/{game_history_tile.dart => match_tile.dart} (95%) diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index 3e221e7..8f911cd 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -6,8 +6,8 @@ import 'package:game_tracker/data/dto/match.dart'; import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/buttons/quick_create_button.dart'; -import 'package:game_tracker/presentation/widgets/tiles/game_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart'; +import 'package:game_tracker/presentation/widgets/tiles/match_summary_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/quick_info_tile.dart'; import 'package:provider/provider.dart'; @@ -112,13 +112,13 @@ class _HomeViewState extends State { visible: !isLoading && loadedRecentMatches.isNotEmpty, replacement: const Center( heightFactor: 12, - child: Text('No recent games available'), + child: Text('No recent matches available'), ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - MatchTile( + MatchSummaryTile( matchTitle: recentMatches[0].name, game: 'Winner', ruleset: 'Ruleset', @@ -132,20 +132,20 @@ class _HomeViewState extends State { child: Divider(), ), if (loadedRecentMatches.length > 1) ...[ - MatchTile( + MatchSummaryTile( matchTitle: recentMatches[1].name, game: 'Winner', ruleset: 'Ruleset', players: _getPlayerText(recentMatches[1]), winner: recentMatches[1].winner == null - ? 'Game in progress...' + ? 'Match in progress...' : recentMatches[1].winner!.name, ), const SizedBox(height: 8), ] else ...[ const Center( heightFactor: 5.35, - child: Text('No second game available'), + child: Text('No second match available'), ), ], ], diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index e7d29c0..a5f6200 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -12,7 +12,7 @@ import 'package:game_tracker/presentation/views/main_menu/match_view/create_matc import 'package:game_tracker/presentation/views/main_menu/match_view/match_result_view.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart'; -import 'package:game_tracker/presentation/widgets/tiles/game_history_tile.dart'; +import 'package:game_tracker/presentation/widgets/tiles/match_tile.dart'; import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:provider/provider.dart'; @@ -30,9 +30,9 @@ class _MatchViewState extends State { List matches = List.filled( 4, Match( - name: 'Skeleton Gamename', + name: 'Skeleton match name', group: Group( - name: 'Groupname', + name: 'Group name', members: List.filled(5, Player(name: 'Player')), ), winner: Player(name: 'Player'), @@ -74,7 +74,7 @@ class _MatchViewState extends State { height: MediaQuery.paddingOf(context).bottom - 80, ); } - return GameHistoryTile( + return MatchTile( onTap: () async { Navigator.push( context, @@ -96,7 +96,7 @@ class _MatchViewState extends State { Positioned( bottom: MediaQuery.paddingOf(context).bottom, child: CustomWidthButton( - text: 'Create Game', + text: 'Create Match', sizeRelativeToWidth: 0.90, onPressed: () async { Navigator.push( diff --git a/lib/presentation/widgets/tiles/game_tile.dart b/lib/presentation/widgets/tiles/match_summary_tile.dart similarity index 92% rename from lib/presentation/widgets/tiles/game_tile.dart rename to lib/presentation/widgets/tiles/match_summary_tile.dart index f98f425..719037b 100644 --- a/lib/presentation/widgets/tiles/game_tile.dart +++ b/lib/presentation/widgets/tiles/match_summary_tile.dart @@ -2,14 +2,14 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:skeletonizer/skeletonizer.dart'; -class MatchTile extends StatefulWidget { +class MatchSummaryTile extends StatefulWidget { final String matchTitle; final String game; final String ruleset; final String players; final String winner; - const MatchTile({ + const MatchSummaryTile({ super.key, required this.matchTitle, required this.game, @@ -19,10 +19,10 @@ class MatchTile extends StatefulWidget { }); @override - State createState() => _MatchTileState(); + State createState() => _MatchSummaryTileState(); } -class _MatchTileState extends State { +class _MatchSummaryTileState extends State { @override Widget build(BuildContext context) { return Column( diff --git a/lib/presentation/widgets/tiles/game_history_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart similarity index 95% rename from lib/presentation/widgets/tiles/game_history_tile.dart rename to lib/presentation/widgets/tiles/match_tile.dart index f79edc3..543a542 100644 --- a/lib/presentation/widgets/tiles/game_history_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -4,17 +4,17 @@ import 'package:game_tracker/data/dto/match.dart'; import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart'; import 'package:intl/intl.dart'; -class GameHistoryTile extends StatefulWidget { +class MatchTile extends StatefulWidget { final Match match; final VoidCallback onTap; - const GameHistoryTile({super.key, required this.match, required this.onTap}); + const MatchTile({super.key, required this.match, required this.onTap}); @override - State createState() => _GameHistoryTileState(); + State createState() => _MatchTileState(); } -class _GameHistoryTileState extends State { +class _MatchTileState extends State { @override Widget build(BuildContext context) { final group = widget.match.group; From b0073addf84062cf7c3fc99d4bfbc003aa95fce8 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Thu, 1 Jan 2026 22:45:55 +0100 Subject: [PATCH 08/12] remove unnecessary trim --- .../main_menu/match_view/create_match/create_match_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index 6ef53ba..a91a45b 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -234,7 +234,7 @@ class _CreateMatchViewState extends State { ? () async { Match match = Match( name: _matchNameController.text.isEmpty - ? hintText.trim() + ? hintText : _matchNameController.text.trim(), createdAt: DateTime.now(), group: selectedGroup, From 304fa82a93612401260c8159e3f117f114e3ab9c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 1 Jan 2026 23:14:52 +0100 Subject: [PATCH 09/12] Fixed bug in player selection --- .../widgets/player_selection.dart | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index d497fcd..c7191a4 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -12,13 +12,13 @@ import 'package:provider/provider.dart'; class PlayerSelection extends StatefulWidget { final Function(List value) onChanged; - final List availablePlayers; + final List? availablePlayers; final List? initialSelectedPlayers; const PlayerSelection({ super.key, required this.onChanged, - this.availablePlayers = const [], + this.availablePlayers, this.initialSelectedPlayers, }); @@ -56,17 +56,17 @@ class _PlayerSelectionState extends State { if (mounted) { _allPlayersFuture.then((loadedPlayers) { setState(() { - // If a list of available players is provided, use that list. - if (widget.availablePlayers.isNotEmpty) { - widget.availablePlayers.sort((a, b) => a.name.compareTo(b.name)); - allPlayers = [...widget.availablePlayers]; + // If a list of available players is provided (even if empty), use that list. + if (widget.availablePlayers != null) { + widget.availablePlayers!.sort((a, b) => a.name.compareTo(b.name)); + allPlayers = [...widget.availablePlayers!]; suggestedPlayers = [...allPlayers]; if (widget.initialSelectedPlayers != null) { // Ensures that only players available for selection are pre-selected. selectedPlayers = widget.initialSelectedPlayers! .where( - (p) => widget.availablePlayers.any( + (p) => widget.availablePlayers!.any( (available) => available.id == p.id, ), ) @@ -193,11 +193,7 @@ class _PlayerSelectionState extends State { replacement: TopCenteredMessage( icon: Icons.info, title: 'Info', - message: allPlayers.isEmpty - ? 'No players created yet' - : (selectedPlayers.length == allPlayers.length) - ? 'No more players to add' - : 'No players found with that name', + message: _getInfoText(), ), child: ListView.builder( itemCount: suggestedPlayers.length, @@ -273,4 +269,22 @@ class _PlayerSelectionState extends State { ); } } + + /// Determines the appropriate info text to display when no players + /// are available in the suggested players list. + String _getInfoText() { + if (widget.availablePlayers != null && widget.availablePlayers!.isEmpty) { + // Available players list is provided but empty + return 'All players added to the match'; + } else if (allPlayers.isEmpty) { + // No players exist in the database + return 'No players created yet'; + } else if (selectedPlayers.length == allPlayers.length) { + // All players have been selected + return 'No more players to add'; + } else { + // No players match the search query + return 'No players found with that name'; + } + } } From cb7804cf55a66b2e5bc6a9fd367d1256e9f9de29 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 2 Jan 2026 18:38:13 +0100 Subject: [PATCH 10/12] Fixed Problem with widget state --- .../main_menu/match_view/create_match/create_match_view.dart | 4 +++- lib/presentation/widgets/player_selection.dart | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index a91a45b..9923d7a 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -110,8 +110,10 @@ class _CreateMatchViewState extends State { ]).then((result) async { groupsList = result[0] as List; playerList = result[1] as List; + setState(() { + filteredPlayerList = List.from(playerList); + }); }); - filteredPlayerList = List.from(playerList); } @override diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index c7191a4..112df65 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -182,9 +182,6 @@ class _PlayerSelectionState extends State { style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 10), - /* - - */ Expanded( child: AppSkeleton( enabled: isLoading, From 87856e6548d4a8ed9595d3f752b709ecf081f690 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 2 Jan 2026 18:41:39 +0100 Subject: [PATCH 11/12] Simplified info texts --- lib/presentation/widgets/player_selection.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 112df65..e9aacd3 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -270,14 +270,13 @@ class _PlayerSelectionState extends State { /// Determines the appropriate info text to display when no players /// are available in the suggested players list. String _getInfoText() { - if (widget.availablePlayers != null && widget.availablePlayers!.isEmpty) { - // Available players list is provided but empty - return 'All players added to the match'; - } else if (allPlayers.isEmpty) { + if (allPlayers.isEmpty) { // No players exist in the database return 'No players created yet'; - } else if (selectedPlayers.length == allPlayers.length) { - // All players have been selected + } else if (selectedPlayers.length == allPlayers.length || + widget.availablePlayers?.isEmpty == true) { + // All players have been selected or + // available players list is provided but empty return 'No more players to add'; } else { // No players match the search query From bdc3453d7fc9072bf8951314e2b8c204d3e8f357 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Fri, 2 Jan 2026 21:20:30 +0100 Subject: [PATCH 12/12] correct spelling mistake --- lib/presentation/widgets/player_selection.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index e9aacd3..6ca6373 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -224,7 +224,7 @@ class _PlayerSelectionState extends State { } /// Adds a new player to the database from the search bar input. - /// Shows a snackbar indicating success xfor failure. + /// Shows a snackbar indicating success or failure. /// [context] - BuildContext to show the snackbar. void addNewPlayerFromSearch({required BuildContext context}) async { String playerName = _searchBarController.text.trim();