diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 5a554c5..ef8d4b5 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -22,11 +22,15 @@ "days_ago": "vor {count} Tagen", "delete": "Löschen", "delete_all_data": "Alle Daten löschen", + "delete_group": "Diese Gruppe löschen", + "edit_group": "Gruppe bearbeiten", "delete_group": "Gruppe löschen", "delete_match": "Spiel löschen", "edit_group": "Gruppe bearbeiten", "enter_results": "Ergebnisse eintragen", "error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen", + "error_deleting_group": "Fehler beim Löschen der Gruppe, bitte erneut versuchen", + "error_editing_group": "Fehler beim Bearbeiten der Gruppe, bitte erneut versuchen", "error_reading_file": "Fehler beim Lesen der Datei", "export_canceled": "Export abgebrochen", "export_data": "Daten exportieren", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index c818920..18e9026 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -37,10 +37,10 @@ "description": "Button text to create a match" }, "@create_new_group": { - "description": "Button text to create a new group" + "description": "Appbar text to create a new group" }, "@create_new_match": { - "description": "Button text to create a new match" + "description": "Appbar text to create a new match" }, "@created_on": { "description": "Label for creation date" @@ -72,13 +72,13 @@ "description": "Confirmation dialog for deleting all data" }, "@delete_group": { - "description": "Button text to delete a group" + "description": "Confirmation dialog for deleting a group" }, "@delete_match": { "description": "Button text to delete a match" }, "@edit_group": { - "description": "Button text to edit a group" + "description": "Button & Appbar label for editing a group" }, "@enter_results": { "description": "Button text to enter match results" @@ -86,6 +86,12 @@ "@error_creating_group": { "description": "Error message when group creation fails" }, + "@error_deleting_group": { + "description": "Error message when group deletion fails" + }, + "@error_editing_group": { + "description": "Error message when group editing fails" + }, "@error_reading_file": { "description": "Error message when file cannot be read" }, @@ -343,6 +349,8 @@ "edit_group": "Edit Group", "enter_results": "Enter Results", "error_creating_group": "Error while creating group, please try again", + "error_deleting_group": "Error while deleting group, please try again", + "error_editing_group": "Error while editing group, please try again", "error_reading_file": "Error reading file", "export_canceled": "Export canceled", "export_data": "Export data", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 49c2e20..250a8fb 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -170,7 +170,7 @@ abstract class AppLocalizations { /// **'Create match'** String get create_match; - /// Button text to create a new group + /// Appbar text to create a new group /// /// In en, this message translates to: /// **'Create new group'** @@ -182,7 +182,7 @@ abstract class AppLocalizations { /// **'Created on'** String get created_on; - /// Button text to create a new match + /// Appbar text to create a new match /// /// In en, this message translates to: /// **'Create new match'** @@ -230,7 +230,7 @@ abstract class AppLocalizations { /// **'Delete all data'** String get delete_all_data; - /// Button text to delete a group + /// Confirmation dialog for deleting a group /// /// In en, this message translates to: /// **'Delete Group'** @@ -242,7 +242,7 @@ abstract class AppLocalizations { /// **'Delete Match'** String get delete_match; - /// Button text to edit a group + /// Button & Appbar label for editing a group /// /// In en, this message translates to: /// **'Edit Group'** @@ -260,6 +260,18 @@ abstract class AppLocalizations { /// **'Error while creating group, please try again'** String get error_creating_group; + /// Error message when group deletion fails + /// + /// In en, this message translates to: + /// **'Error while deleting group, please try again'** + String get error_deleting_group; + + /// Error message when group editing fails + /// + /// In en, this message translates to: + /// **'Error while editing group, please try again'** + String get error_editing_group; + /// Error message when file cannot be read /// /// In en, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 8259aa5..bd9b506 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -94,6 +94,14 @@ class AppLocalizationsDe extends AppLocalizations { String get error_creating_group => 'Fehler beim Erstellen der Gruppe, bitte erneut versuchen'; + @override + String get error_deleting_group => + 'Fehler beim Löschen der Gruppe, bitte erneut versuchen'; + + @override + String get error_editing_group => + 'Fehler beim Bearbeiten der Gruppe, bitte erneut versuchen'; + @override String get error_reading_file => 'Fehler beim Lesen der Datei'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index b66b293..375ea05 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -94,6 +94,14 @@ class AppLocalizationsEn extends AppLocalizations { String get error_creating_group => 'Error while creating group, please try again'; + @override + String get error_deleting_group => + 'Error while deleting group, please try again'; + + @override + String get error_editing_group => + 'Error while editing group, please try again'; + @override String get error_reading_file => 'Error reading file'; diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index a110419..b17f63d 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/adaptive_page_route.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart'; -import 'package:game_tracker/presentation/views/main_menu/group_view/groups_view.dart'; +import 'package:game_tracker/presentation/views/main_menu/group_view/group_view.dart'; import 'package:game_tracker/presentation/views/main_menu/home_view.dart'; import 'package:game_tracker/presentation/views/main_menu/match_view/match_view.dart'; import 'package:game_tracker/presentation/views/main_menu/settings_view/settings_view.dart'; diff --git a/lib/presentation/views/main_menu/group_view/create_group_view.dart b/lib/presentation/views/main_menu/group_view/create_group_view.dart index da7eb1d..678872a 100644 --- a/lib/presentation/views/main_menu/group_view/create_group_view.dart +++ b/lib/presentation/views/main_menu/group_view/create_group_view.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:game_tracker/core/constants.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/enums.dart'; import 'package:game_tracker/data/db/database.dart'; @@ -12,8 +11,10 @@ import 'package:game_tracker/presentation/widgets/text_input/text_input_field.da import 'package:provider/provider.dart'; class CreateGroupView extends StatefulWidget { - /// A view that allows the user to create a new group - const CreateGroupView({super.key}); + const CreateGroupView({super.key, this.groupToEdit}); + + /// The group to edit, if any + final Group? groupToEdit; @override State createState() => _CreateGroupViewState(); @@ -22,16 +23,29 @@ class CreateGroupView extends StatefulWidget { class _CreateGroupViewState extends State { late final AppDatabase db; + /// GlobalKey for ScaffoldMessenger to show snackbars + final _scaffoldMessengerKey = GlobalKey(); + /// Controller for the group name input field final _groupNameController = TextEditingController(); /// List of currently selected players List selectedPlayers = []; + /// List of initially selected players (when editing a group) + List initialSelectedPlayers = []; + @override void initState() { super.initState(); db = Provider.of(context, listen: false); + if(widget.groupToEdit != null) { + _groupNameController.text = widget.groupToEdit!.name; + setState(() { + initialSelectedPlayers = widget.groupToEdit!.members; + selectedPlayers = widget.groupToEdit!.members; + }); + } _groupNameController.addListener(() { setState(() {}); }); @@ -47,9 +61,42 @@ class _CreateGroupViewState extends State { Widget build(BuildContext context) { final loc = AppLocalizations.of(context); return ScaffoldMessenger( + key: _scaffoldMessengerKey, child: Scaffold( + resizeToAvoidBottomInset: false, backgroundColor: CustomTheme.backgroundColor, - appBar: AppBar(title: Text(loc.create_new_group)), + appBar: AppBar(title: Text(widget.groupToEdit == null ? loc.create_new_group : loc.edit_group), actions: widget.groupToEdit == null ? [] : [IconButton(icon: const Icon(Icons.delete), onPressed: () async { + if(widget.groupToEdit != null) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(loc.delete_group), + content: Text(loc.this_cannot_be_undone), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text(loc.cancel), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text(loc.delete), + ), + ], + ), + ).then((confirmed) async { + if (confirmed == true && context.mounted) { + bool success = await db.groupDao.deleteGroup(groupId: widget.groupToEdit!.id); + if (!context.mounted) return; + if (success) { + Navigator.pop(context); + } else { + if (!mounted) return; + showSnackbar(message: loc.error_deleting_group); + } + } + }); + } + },)],), body: SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.start, @@ -59,11 +106,11 @@ class _CreateGroupViewState extends State { child: TextInputField( controller: _groupNameController, hintText: loc.group_name, - maxLength: Constants.MAX_GROUP_NAME_LENGTH, ), ), Expanded( child: PlayerSelection( + initialSelectedPlayers: initialSelectedPlayers, onChanged: (value) { setState(() { selectedPlayers = [...value]; @@ -72,7 +119,7 @@ class _CreateGroupViewState extends State { ), ), CustomWidthButton( - text: loc.create_group, + text: widget.groupToEdit == null ? loc.create_group : loc.edit_group, sizeRelativeToWidth: 0.95, buttonType: ButtonType.primary, onPressed: @@ -80,29 +127,34 @@ class _CreateGroupViewState extends State { (selectedPlayers.length < 2)) ? null : () async { - bool success = await db.groupDao.addGroup( - group: Group( - name: _groupNameController.text.trim(), - members: selectedPlayers, - ), - ); - if (!context.mounted) return; - if (success) { - Navigator.pop(context); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - backgroundColor: CustomTheme.boxColor, - content: Center( - child: Text( - AppLocalizations.of( - context, - ).error_creating_group, - style: const TextStyle(color: Colors.white), - ), - ), + late Group? updatedGroup; + late bool success; + if (widget.groupToEdit == null) { + success = await db.groupDao.addGroup( + group: Group( + name: _groupNameController.text.trim(), + members: selectedPlayers, ), ); + } else { + updatedGroup = Group( + id: widget.groupToEdit!.id, + name: _groupNameController.text.trim(), + members: selectedPlayers, + ); + //TODO: Implement group editing in database + /* + success = await db.groupDao.updateGroup( + group: updatedGroup, + ); + */ + success = true; + } + if (!context.mounted) return; + if (success) { + Navigator.pop(context, updatedGroup); + } else { + showSnackbar(message: widget.groupToEdit == null ? loc.error_creating_group : loc.error_editing_group); } }, ), @@ -113,4 +165,21 @@ class _CreateGroupViewState extends State { ), ); } + /// Displays a snackbar with the given message and optional action. + /// + /// [message] The message to display in the snackbar. + void showSnackbar({ + required String message, + }) { + final messenger = _scaffoldMessengerKey.currentState; + if (messenger != null) { + messenger.hideCurrentSnackBar(); + messenger.showSnackBar( + SnackBar( + content: Text(message, style: const TextStyle(color: Colors.white)), + backgroundColor: CustomTheme.boxColor, + ), + ); + } + } } diff --git a/lib/presentation/views/main_menu/group_view/group_profile_view.dart b/lib/presentation/views/main_menu/group_view/group_detail_view.dart similarity index 85% rename from lib/presentation/views/main_menu/group_view/group_profile_view.dart rename to lib/presentation/views/main_menu/group_view/group_detail_view.dart index 7c4102c..d5fb65b 100644 --- a/lib/presentation/views/main_menu/group_view/group_profile_view.dart +++ b/lib/presentation/views/main_menu/group_view/group_detail_view.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:game_tracker/core/adaptive_page_route.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/match.dart'; import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart'; +import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/buttons/animated_dialog_button.dart'; import 'package:game_tracker/presentation/widgets/buttons/main_menu_button.dart'; @@ -15,10 +17,10 @@ import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -class GroupProfileView extends StatefulWidget { +class GroupDetailView extends StatefulWidget { /// A view that displays the profile of a group /// - [group]: The group to display - const GroupProfileView({ + const GroupDetailView({ super.key, required this.group, required this.callback, @@ -30,12 +32,13 @@ class GroupProfileView extends StatefulWidget { final VoidCallback callback; @override - State createState() => _GroupProfileViewState(); + State createState() => _GroupDetailViewState(); } -class _GroupProfileViewState extends State { +class _GroupDetailViewState extends State { late final AppDatabase db; bool isLoading = true; + late Group _group; /// Total matches played in this group int totalMatches = 0; @@ -46,6 +49,7 @@ class _GroupProfileViewState extends State { @override void initState() { super.initState(); + _group = widget.group; db = Provider.of(context, listen: false); _loadStatistics(); } @@ -86,7 +90,7 @@ class _GroupProfileViewState extends State { ), ).then((confirmed) async { if (confirmed! && context.mounted) { - await db.groupDao.deleteGroup(groupId: widget.group.id); + await db.groupDao.deleteGroup(groupId: _group.id); if (!context.mounted) return; Navigator.pop(context); widget.callback.call(); @@ -117,7 +121,7 @@ class _GroupProfileViewState extends State { ), const SizedBox(height: 10), Text( - widget.group.name, + _group.name, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.bold, @@ -127,7 +131,7 @@ class _GroupProfileViewState extends State { ), const SizedBox(height: 5), Text( - '${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(widget.group.createdAt)}', + '${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(_group.createdAt)}', style: const TextStyle( fontSize: 12, color: CustomTheme.textColor, @@ -144,7 +148,7 @@ class _GroupProfileViewState extends State { crossAxisAlignment: WrapCrossAlignment.start, spacing: 12, runSpacing: 8, - children: widget.group.members.map((member) { + children: _group.members.map((member) { return TextIconTile( text: member.name, iconEnabled: false, @@ -162,7 +166,7 @@ class _GroupProfileViewState extends State { children: [ _buildStatRow( loc.members, - widget.group.members.length.toString(), + _group.members.length.toString(), ), _buildStatRow( loc.played_matches, @@ -180,19 +184,24 @@ class _GroupProfileViewState extends State { child: MainMenuButton( text: loc.edit_group, icon: Icons.edit, - onPressed: () { - // TODO: Uncomment when GroupDetailView is implemented - /* - await Navigator.push( + onPressed: () async { + final updatedGroup = await Navigator.push( context, adaptivePageRoute( builder: (context) { - - return const GroupDetailView(); + return CreateGroupView( + groupToEdit: _group, + ); }, ), - );*/ - print('Edit Group pressed'); + ); + if (updatedGroup != null && mounted) { + setState(() { + _group = updatedGroup; + }); + _loadStatistics(); + widget.callback(); + } }, ), ), @@ -234,9 +243,8 @@ class _GroupProfileViewState extends State { /// Loads statistics for this group Future _loadStatistics() async { final matches = await db.matchDao.getAllMatches(); - final groupMatches = matches - .where((match) => match.group?.id == widget.group.id) - .toList(); + final groupMatches = + matches.where((match) => match.group?.id == _group.id).toList(); setState(() { totalMatches = groupMatches.length; @@ -254,7 +262,7 @@ class _GroupProfileViewState extends State { if (match.winner != null) { bestPlayerCounts.update( match.winner!, - (value) => value + 1, + (value) => value + 1, ifAbsent: () => 1, ); } @@ -269,4 +277,4 @@ class _GroupProfileViewState extends State { return bestPlayer; } -} +} \ No newline at end of file diff --git a/lib/presentation/views/main_menu/group_view/groups_view.dart b/lib/presentation/views/main_menu/group_view/group_view.dart similarity index 98% rename from lib/presentation/views/main_menu/group_view/groups_view.dart rename to lib/presentation/views/main_menu/group_view/group_view.dart index 81922f5..a995f12 100644 --- a/lib/presentation/views/main_menu/group_view/groups_view.dart +++ b/lib/presentation/views/main_menu/group_view/group_view.dart @@ -6,8 +6,8 @@ import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart'; +import 'package:game_tracker/presentation/views/main_menu/group_view/group_detail_view.dart'; import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart'; -import 'package:game_tracker/presentation/views/main_menu/group_view/group_profile_view.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/buttons/main_menu_button.dart'; import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart'; @@ -82,7 +82,7 @@ class _GroupsViewState extends State { context, adaptivePageRoute( builder: (context) { - return GroupProfileView( + return GroupDetailView( group: groups[index], callback: loadGroups, ); diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart index 32868e4..3ff6e79 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart @@ -43,6 +43,7 @@ class _ChooseGameViewState extends State { final loc = AppLocalizations.of(context); return Scaffold( backgroundColor: CustomTheme.backgroundColor, + resizeToAvoidBottomInset: false, appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_ios), diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart index 00a0276..592d765 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart @@ -43,6 +43,7 @@ class _ChooseGroupViewState extends State { final loc = AppLocalizations.of(context); return Scaffold( backgroundColor: CustomTheme.backgroundColor, + resizeToAvoidBottomInset: false, appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_ios), 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 fa685c3..39438a1 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 @@ -67,6 +67,9 @@ class _CreateMatchViewState extends State { /// The currently selected players List? selectedPlayers; + /// GlobalKey for ScaffoldMessenger to show snackbars + final _scaffoldMessengerKey = GlobalKey(); + @override void initState() { super.initState(); @@ -128,7 +131,9 @@ class _CreateMatchViewState extends State { : loc.create_match; return ScaffoldMessenger( + key: _scaffoldMessengerKey, child: Scaffold( + resizeToAvoidBottomInset: false, backgroundColor: CustomTheme.backgroundColor, appBar: AppBar(title: Text(loc.create_new_match)), body: SafeArea( diff --git a/lib/presentation/views/main_menu/settings_view/settings_view.dart b/lib/presentation/views/main_menu/settings_view/settings_view.dart index 7941cb5..78c7efc 100644 --- a/lib/presentation/views/main_menu/settings_view/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view/settings_view.dart @@ -31,6 +31,7 @@ class _SettingsViewState extends State { version: 'n.A.', buildNumber: 'n.A.', ); + @override void initState() { super.initState(); diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 587c8af..784b973 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -70,6 +70,7 @@ class _PlayerSelectionState extends State { super.initState(); db = Provider.of(context, listen: false); suggestedPlayers = skeletonData; + selectedPlayers = widget.initialSelectedPlayers ?? []; loadPlayerList(); } @@ -99,7 +100,7 @@ class _PlayerSelectionState extends State { if (value.isEmpty) { // If the search is empty, it shows all unselected players. suggestedPlayers = allPlayers.where((player) { - return !selectedPlayers.contains(player); + return !selectedPlayers.any((p) => p.id == player.id); }).toList(); } else { // If there is input, it filters by name match (case-insensitive) and ensures @@ -108,9 +109,7 @@ class _PlayerSelectionState extends State { final bool nameMatches = player.name.toLowerCase().contains( value.toLowerCase(), ); - final bool isNotSelected = !selectedPlayers.contains( - player, - ); + final bool isNotSelected = !selectedPlayers.any((p) => p.id == player.id); return nameMatches && isNotSelected; }).toList(); } @@ -125,46 +124,49 @@ class _PlayerSelectionState extends State { const SizedBox(height: 10), SizedBox( height: 50, - child: selectedPlayers.isEmpty - ? Center(child: Text(loc.no_players_selected)) - : SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - for (var player in selectedPlayers) - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: TextIconTile( - text: player.name, - onIconTap: () { - setState(() { - // Removes the player from the selection and notifies the parent. - selectedPlayers.remove(player); - widget.onChanged([...selectedPlayers]); + child: AppSkeleton( + enabled: isLoading, + child: selectedPlayers.isEmpty + ? Center(child: Text(loc.no_players_selected)) + : SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + for (var player in selectedPlayers) + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: TextIconTile( + text: player.name, + 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(); + // Get the current search query + final currentSearch = _searchBarController + .text + .toLowerCase(); - // If the player matches the current search query (or search is empty), - // 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), - ); - } - }); - }, + // If the player matches the current search query (or search is empty), + // 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), + ); + } + }); + }, + ), ), - ), - ], + ], + ), ), - ), + ), ), const SizedBox(height: 10), Text( @@ -244,7 +246,21 @@ class _PlayerSelectionState extends State { // Otherwise, use the loaded players from the database. loadedPlayers.sort((a, b) => a.name.compareTo(b.name)); allPlayers = [...loadedPlayers]; - suggestedPlayers = [...loadedPlayers]; + if (widget.initialSelectedPlayers != null) { + // Excludes already selected players from the suggested players list. + suggestedPlayers = loadedPlayers.where((p) => !widget.initialSelectedPlayers!.any((ip) => ip.id == p.id)).toList(); + // Ensures that only players available for selection are pre-selected. + selectedPlayers = widget.initialSelectedPlayers! + .where( + (p) => allPlayers.any( + (available) => available.id == p.id, + ), + ) + .toList(); + } else { + // If no initial selection, all loaded players are suggested. + suggestedPlayers = [...loadedPlayers]; + } } isLoading = false; });