feat: new team member selection
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"@@locale": "de",
|
||||
"add_team": "Team hinzufügen",
|
||||
"all_players": "Alle Spieler:innen",
|
||||
"all_players_selected": "Alle Spieler:innen ausgewählt",
|
||||
"amount_of_matches": "Anzahl der Spiele",
|
||||
@@ -50,7 +51,6 @@
|
||||
"edit_game": "Spielvorlage bearbeiten",
|
||||
"edit_group": "Gruppe bearbeiten",
|
||||
"edit_match": "Gruppe bearbeiten",
|
||||
"edit_members": "Mitglieder bearbeiten",
|
||||
"enter_points": "Punkte eingeben",
|
||||
"enter_results": "Ergebnisse eintragen",
|
||||
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
|
||||
@@ -81,6 +81,7 @@
|
||||
"live_edit_mode": "Live-Bearbeitungsmodus",
|
||||
"loser": "Verlierer:in",
|
||||
"lowest_score": "Niedrigste Punkte",
|
||||
"manage_members": "Mitglieder bearbeiten",
|
||||
"match_in_progress": "Spiel läuft...",
|
||||
"match_name": "Spieltitel",
|
||||
"match_profile": "Spielprofil",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
"add_team": "Add Team",
|
||||
"all_players": "All players",
|
||||
"all_players_selected": "All players selected",
|
||||
"amount_of_matches": "Amount of Matches",
|
||||
@@ -50,7 +51,6 @@
|
||||
"edit_game": "Edit Game",
|
||||
"edit_group": "Edit Group",
|
||||
"edit_match": "Edit Match",
|
||||
"edit_members": "Edit Members",
|
||||
"enter_points": "Enter points",
|
||||
"enter_results": "Enter Results",
|
||||
"error_creating_group": "Error while creating group, please try again",
|
||||
@@ -81,6 +81,7 @@
|
||||
"live_edit_mode": "Live Edit Mode",
|
||||
"loser": "Loser",
|
||||
"lowest_score": "Lowest Score",
|
||||
"manage_members": "Manage Members",
|
||||
"match_in_progress": "Match in progress...",
|
||||
"match_name": "Match name",
|
||||
"match_profile": "Match Profile",
|
||||
|
||||
@@ -98,6 +98,12 @@ abstract class AppLocalizations {
|
||||
Locale('en'),
|
||||
];
|
||||
|
||||
/// No description provided for @add_team.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add Team'**
|
||||
String get add_team;
|
||||
|
||||
/// No description provided for @all_players.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -242,12 +248,6 @@ abstract class AppLocalizations {
|
||||
/// **'Create new group'**
|
||||
String get create_new_group;
|
||||
|
||||
/// No description provided for @created_on.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Created on'**
|
||||
String get created_on;
|
||||
|
||||
/// No description provided for @create_new_match.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -260,6 +260,12 @@ abstract class AppLocalizations {
|
||||
/// **'Create teams'**
|
||||
String get create_teams;
|
||||
|
||||
/// No description provided for @created_on.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Created on'**
|
||||
String get created_on;
|
||||
|
||||
/// No description provided for @data.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -326,18 +332,18 @@ abstract class AppLocalizations {
|
||||
/// **'Delete Match'**
|
||||
String get delete_match;
|
||||
|
||||
/// No description provided for @drag_to_set_placement.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Drag to set placement'**
|
||||
String get drag_to_set_placement;
|
||||
|
||||
/// No description provided for @description.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Description'**
|
||||
String get description;
|
||||
|
||||
/// No description provided for @drag_to_set_placement.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Drag to set placement'**
|
||||
String get drag_to_set_placement;
|
||||
|
||||
/// No description provided for @edit_game.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -356,12 +362,6 @@ abstract class AppLocalizations {
|
||||
/// **'Edit Match'**
|
||||
String get edit_match;
|
||||
|
||||
/// No description provided for @edit_members.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit Members'**
|
||||
String get edit_members;
|
||||
|
||||
/// No description provided for @enter_points.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -464,6 +464,12 @@ abstract class AppLocalizations {
|
||||
/// **'Groups'**
|
||||
String get groups;
|
||||
|
||||
/// No description provided for @highest_score.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Highest Score'**
|
||||
String get highest_score;
|
||||
|
||||
/// No description provided for @home.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -524,6 +530,24 @@ abstract class AppLocalizations {
|
||||
/// **'Live Edit Mode'**
|
||||
String get live_edit_mode;
|
||||
|
||||
/// No description provided for @loser.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Loser'**
|
||||
String get loser;
|
||||
|
||||
/// No description provided for @lowest_score.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Lowest Score'**
|
||||
String get lowest_score;
|
||||
|
||||
/// No description provided for @manage_members.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Manage Members'**
|
||||
String get manage_members;
|
||||
|
||||
/// No description provided for @match_in_progress.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -560,6 +584,12 @@ abstract class AppLocalizations {
|
||||
/// **'Most Points'**
|
||||
String get most_points;
|
||||
|
||||
/// No description provided for @multiple_winners.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Multiple Winners'**
|
||||
String get multiple_winners;
|
||||
|
||||
/// No description provided for @no_data_available.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -578,18 +608,18 @@ abstract class AppLocalizations {
|
||||
/// **'No groups created yet'**
|
||||
String get no_groups_created_yet;
|
||||
|
||||
/// No description provided for @no_licenses_found.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No licenses found'**
|
||||
String get no_licenses_found;
|
||||
|
||||
/// No description provided for @no_license_text_available.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No license text available'**
|
||||
String get no_license_text_available;
|
||||
|
||||
/// No description provided for @no_licenses_found.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No licenses found'**
|
||||
String get no_licenses_found;
|
||||
|
||||
/// No description provided for @no_matches_created_yet.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -656,18 +686,18 @@ abstract class AppLocalizations {
|
||||
/// **'Not available'**
|
||||
String get not_available;
|
||||
|
||||
/// No description provided for @placement.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Placement'**
|
||||
String get placement;
|
||||
|
||||
/// No description provided for @place.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'place'**
|
||||
String get place;
|
||||
|
||||
/// No description provided for @placement.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Placement'**
|
||||
String get placement;
|
||||
|
||||
/// No description provided for @played_matches.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -782,6 +812,12 @@ abstract class AppLocalizations {
|
||||
/// **'Search for players'**
|
||||
String get search_for_players;
|
||||
|
||||
/// No description provided for @select_loser.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Loser'**
|
||||
String get select_loser;
|
||||
|
||||
/// No description provided for @select_winner.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -794,12 +830,6 @@ abstract class AppLocalizations {
|
||||
/// **'Select Winners'**
|
||||
String get select_winners;
|
||||
|
||||
/// No description provided for @select_loser.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Loser'**
|
||||
String get select_loser;
|
||||
|
||||
/// No description provided for @selected_players.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -824,30 +854,6 @@ abstract class AppLocalizations {
|
||||
/// **'Single Winner'**
|
||||
String get single_winner;
|
||||
|
||||
/// No description provided for @highest_score.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Highest Score'**
|
||||
String get highest_score;
|
||||
|
||||
/// No description provided for @loser.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Loser'**
|
||||
String get loser;
|
||||
|
||||
/// No description provided for @lowest_score.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Lowest Score'**
|
||||
String get lowest_score;
|
||||
|
||||
/// No description provided for @multiple_winners.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Multiple Winners'**
|
||||
String get multiple_winners;
|
||||
|
||||
/// No description provided for @statistics.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -8,6 +8,9 @@ import 'app_localizations.dart';
|
||||
class AppLocalizationsDe extends AppLocalizations {
|
||||
AppLocalizationsDe([String locale = 'de']) : super(locale);
|
||||
|
||||
@override
|
||||
String get add_team => 'Team hinzufügen';
|
||||
|
||||
@override
|
||||
String get all_players => 'Alle Spieler:innen';
|
||||
|
||||
@@ -82,15 +85,15 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get create_new_group => 'Neue Gruppe erstellen';
|
||||
|
||||
@override
|
||||
String get created_on => 'Erstellt am';
|
||||
|
||||
@override
|
||||
String get create_new_match => 'Neues Spiel erstellen';
|
||||
|
||||
@override
|
||||
String get create_teams => 'Teams erstellen';
|
||||
|
||||
@override
|
||||
String get created_on => 'Erstellt am';
|
||||
|
||||
@override
|
||||
String get data => 'Daten';
|
||||
|
||||
@@ -135,10 +138,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get delete_match => 'Spiel löschen';
|
||||
|
||||
@override
|
||||
String get drag_to_set_placement => 'Ziehen um Platzierung zu setzen';
|
||||
String get description => 'Beschreibung';
|
||||
|
||||
@override
|
||||
String get description => 'Beschreibung';
|
||||
String get drag_to_set_placement => 'Ziehen um Platzierung zu setzen';
|
||||
|
||||
@override
|
||||
String get edit_game => 'Spielvorlage bearbeiten';
|
||||
@@ -149,9 +152,6 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get edit_match => 'Gruppe bearbeiten';
|
||||
|
||||
@override
|
||||
String get edit_members => 'Mitglieder bearbeiten';
|
||||
|
||||
@override
|
||||
String get enter_points => 'Punkte eingeben';
|
||||
|
||||
@@ -207,6 +207,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get groups => 'Gruppen';
|
||||
|
||||
@override
|
||||
String get highest_score => 'Höchste Punkte';
|
||||
|
||||
@override
|
||||
String get home => 'Startseite';
|
||||
|
||||
@@ -237,6 +240,15 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get live_edit_mode => 'Live-Bearbeitungsmodus';
|
||||
|
||||
@override
|
||||
String get loser => 'Verlierer:in';
|
||||
|
||||
@override
|
||||
String get lowest_score => 'Niedrigste Punkte';
|
||||
|
||||
@override
|
||||
String get manage_members => 'Mitglieder bearbeiten';
|
||||
|
||||
@override
|
||||
String get match_in_progress => 'Spiel läuft...';
|
||||
|
||||
@@ -255,6 +267,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get most_points => 'Höchste Punkte';
|
||||
|
||||
@override
|
||||
String get multiple_winners => 'Mehrere Gewinner:innen';
|
||||
|
||||
@override
|
||||
String get no_data_available => 'Keine Daten verfügbar';
|
||||
|
||||
@@ -265,10 +280,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get no_groups_created_yet => 'Noch keine Gruppen erstellt';
|
||||
|
||||
@override
|
||||
String get no_licenses_found => 'Keine Lizenzen gefunden';
|
||||
String get no_license_text_available => 'Kein Lizenztext verfügbar';
|
||||
|
||||
@override
|
||||
String get no_license_text_available => 'Kein Lizenztext verfügbar';
|
||||
String get no_licenses_found => 'Keine Lizenzen gefunden';
|
||||
|
||||
@override
|
||||
String get no_matches_created_yet => 'Noch keine Spiele erstellt';
|
||||
@@ -305,10 +320,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get not_available => 'Nicht verfügbar';
|
||||
|
||||
@override
|
||||
String get placement => 'Platzierung';
|
||||
String get place => 'Platz';
|
||||
|
||||
@override
|
||||
String get place => 'Platz';
|
||||
String get placement => 'Platzierung';
|
||||
|
||||
@override
|
||||
String get played_matches => 'Gespielte Spiele';
|
||||
@@ -372,15 +387,15 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get search_for_players => 'Nach Spieler:innen suchen';
|
||||
|
||||
@override
|
||||
String get select_loser => 'Verlierer:in wählen';
|
||||
|
||||
@override
|
||||
String get select_winner => 'Gewinner:in wählen';
|
||||
|
||||
@override
|
||||
String get select_winners => 'Gewinner:innen wählen';
|
||||
|
||||
@override
|
||||
String get select_loser => 'Verlierer:in wählen';
|
||||
|
||||
@override
|
||||
String get selected_players => 'Ausgewählte Spieler:innen';
|
||||
|
||||
@@ -393,18 +408,6 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get single_winner => 'Ein:e Gewinner:in';
|
||||
|
||||
@override
|
||||
String get highest_score => 'Höchste Punkte';
|
||||
|
||||
@override
|
||||
String get loser => 'Verlierer:in';
|
||||
|
||||
@override
|
||||
String get lowest_score => 'Niedrigste Punkte';
|
||||
|
||||
@override
|
||||
String get multiple_winners => 'Mehrere Gewinner:innen';
|
||||
|
||||
@override
|
||||
String get statistics => 'Statistiken';
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ import 'app_localizations.dart';
|
||||
class AppLocalizationsEn extends AppLocalizations {
|
||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
||||
|
||||
@override
|
||||
String get add_team => 'Add Team';
|
||||
|
||||
@override
|
||||
String get all_players => 'All players';
|
||||
|
||||
@@ -82,15 +85,15 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get create_new_group => 'Create new group';
|
||||
|
||||
@override
|
||||
String get created_on => 'Created on';
|
||||
|
||||
@override
|
||||
String get create_new_match => 'Create new match';
|
||||
|
||||
@override
|
||||
String get create_teams => 'Create teams';
|
||||
|
||||
@override
|
||||
String get created_on => 'Created on';
|
||||
|
||||
@override
|
||||
String get data => 'Data';
|
||||
|
||||
@@ -135,10 +138,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get delete_match => 'Delete Match';
|
||||
|
||||
@override
|
||||
String get drag_to_set_placement => 'Drag to set placement';
|
||||
String get description => 'Description';
|
||||
|
||||
@override
|
||||
String get description => 'Description';
|
||||
String get drag_to_set_placement => 'Drag to set placement';
|
||||
|
||||
@override
|
||||
String get edit_game => 'Edit Game';
|
||||
@@ -149,9 +152,6 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get edit_match => 'Edit Match';
|
||||
|
||||
@override
|
||||
String get edit_members => 'Edit Members';
|
||||
|
||||
@override
|
||||
String get enter_points => 'Enter points';
|
||||
|
||||
@@ -207,6 +207,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get groups => 'Groups';
|
||||
|
||||
@override
|
||||
String get highest_score => 'Highest Score';
|
||||
|
||||
@override
|
||||
String get home => 'Home';
|
||||
|
||||
@@ -237,6 +240,15 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get live_edit_mode => 'Live Edit Mode';
|
||||
|
||||
@override
|
||||
String get loser => 'Loser';
|
||||
|
||||
@override
|
||||
String get lowest_score => 'Lowest Score';
|
||||
|
||||
@override
|
||||
String get manage_members => 'Manage Members';
|
||||
|
||||
@override
|
||||
String get match_in_progress => 'Match in progress...';
|
||||
|
||||
@@ -255,6 +267,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get most_points => 'Most Points';
|
||||
|
||||
@override
|
||||
String get multiple_winners => 'Multiple Winners';
|
||||
|
||||
@override
|
||||
String get no_data_available => 'No data available';
|
||||
|
||||
@@ -265,10 +280,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get no_groups_created_yet => 'No groups created yet';
|
||||
|
||||
@override
|
||||
String get no_licenses_found => 'No licenses found';
|
||||
String get no_license_text_available => 'No license text available';
|
||||
|
||||
@override
|
||||
String get no_license_text_available => 'No license text available';
|
||||
String get no_licenses_found => 'No licenses found';
|
||||
|
||||
@override
|
||||
String get no_matches_created_yet => 'No matches created yet';
|
||||
@@ -305,10 +320,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get not_available => 'Not available';
|
||||
|
||||
@override
|
||||
String get placement => 'Placement';
|
||||
String get place => 'place';
|
||||
|
||||
@override
|
||||
String get place => 'place';
|
||||
String get placement => 'Placement';
|
||||
|
||||
@override
|
||||
String get played_matches => 'Played Matches';
|
||||
@@ -372,15 +387,15 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get search_for_players => 'Search for players';
|
||||
|
||||
@override
|
||||
String get select_loser => 'Select Loser';
|
||||
|
||||
@override
|
||||
String get select_winner => 'Select Winner';
|
||||
|
||||
@override
|
||||
String get select_winners => 'Select Winners';
|
||||
|
||||
@override
|
||||
String get select_loser => 'Select Loser';
|
||||
|
||||
@override
|
||||
String get selected_players => 'Selected players';
|
||||
|
||||
@@ -393,18 +408,6 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get single_winner => 'Single Winner';
|
||||
|
||||
@override
|
||||
String get highest_score => 'Highest Score';
|
||||
|
||||
@override
|
||||
String get loser => 'Loser';
|
||||
|
||||
@override
|
||||
String get lowest_score => 'Lowest Score';
|
||||
|
||||
@override
|
||||
String get multiple_winners => 'Multiple Winners';
|
||||
|
||||
@override
|
||||
String get statistics => 'Statistics';
|
||||
|
||||
|
||||
@@ -10,8 +10,7 @@ import 'package:tallee/data/models/match.dart';
|
||||
import 'package:tallee/data/models/player.dart';
|
||||
import 'package:tallee/data/models/team.dart';
|
||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_teams/edit_members_view.dart';
|
||||
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
|
||||
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_teams/manage_members_view.dart';
|
||||
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
||||
import 'package:tallee/presentation/widgets/tiles/team_creation_tile.dart';
|
||||
|
||||
@@ -50,7 +49,6 @@ class _CreateTeamsViewState extends State<CreateTeamsView> {
|
||||
|
||||
// Init the controllers
|
||||
nameController = teams.map(getNewController).toList();
|
||||
redistributePlayers();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -71,36 +69,7 @@ class _CreateTeamsViewState extends State<CreateTeamsView> {
|
||||
return TeamCreationTile(
|
||||
color: teams[index].color,
|
||||
controller: nameController[index],
|
||||
players: teams[index].members,
|
||||
hintText: '${loc.team} ${index + 1}',
|
||||
onEdit: () async {
|
||||
final newPlayers = await Navigator.push(
|
||||
context,
|
||||
adaptivePageRoute(
|
||||
fullscreenDialog: true,
|
||||
builder: (context) => EditMembersView(
|
||||
matchPlayer: widget.match.players,
|
||||
teamMember: teams[index].members,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
// Remove the selected players from every team
|
||||
for (final player in newPlayers) {
|
||||
for (final team in teams) {
|
||||
if (team.members.contains(player)) {
|
||||
team.members.remove(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the selected players to the current team
|
||||
teams[index] = teams[index].copyWith(
|
||||
members: newPlayers,
|
||||
);
|
||||
});
|
||||
},
|
||||
onDelete: teams.length <= 2
|
||||
? null
|
||||
: () => _removeTeam(index),
|
||||
@@ -118,19 +87,10 @@ class _CreateTeamsViewState extends State<CreateTeamsView> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Redistribute
|
||||
MainMenuButton(
|
||||
icon: Icons.cached,
|
||||
text: loc.redistribute,
|
||||
onPressed: () => setState(() {
|
||||
redistributePlayers();
|
||||
}),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
|
||||
// Add new team
|
||||
MainMenuButton(
|
||||
icon: Icons.add,
|
||||
text: loc.add_team,
|
||||
onPressed: teams.length >= widget.match.players.length
|
||||
? null
|
||||
: addTeam,
|
||||
@@ -139,21 +99,19 @@ class _CreateTeamsViewState extends State<CreateTeamsView> {
|
||||
|
||||
// Confirm teams and start match
|
||||
MainMenuButton(
|
||||
icon: Icons.check,
|
||||
onPressed: teams.every((team) => team.members.isNotEmpty)
|
||||
icon: Icons.arrow_forward_sharp,
|
||||
onPressed: teams.length >= 2
|
||||
? () async {
|
||||
final match = await createMatchWithTeams();
|
||||
if (context.mounted) {
|
||||
Navigator.pushAndRemoveUntil(
|
||||
Navigator.push(
|
||||
context,
|
||||
adaptivePageRoute(
|
||||
fullscreenDialog: true,
|
||||
builder: (context) => MatchResultView(
|
||||
builder: (context) => ManageMembersView(
|
||||
match: match,
|
||||
onWinnerChanged: widget.onWinnerChanged,
|
||||
),
|
||||
),
|
||||
(route) => route.isFirst,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -174,7 +132,6 @@ class _CreateTeamsViewState extends State<CreateTeamsView> {
|
||||
final newTeam = getNewTeam();
|
||||
teams.add(newTeam);
|
||||
nameController.add(getNewController(newTeam));
|
||||
redistributePlayers();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -245,25 +202,6 @@ class _CreateTeamsViewState extends State<CreateTeamsView> {
|
||||
}
|
||||
}
|
||||
|
||||
// Iterates through all teams and redistributes players randomly and
|
||||
// as evenly as possible.
|
||||
void redistributePlayers() {
|
||||
for (final team in teams) {
|
||||
team.members.clear();
|
||||
}
|
||||
|
||||
if (matchPlayers.isEmpty || teams.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final shuffledPlayers = [...matchPlayers]..shuffle(random);
|
||||
|
||||
for (int i = 0; i < shuffledPlayers.length; i++) {
|
||||
final teamIndex = i % teams.length;
|
||||
teams[teamIndex].members.add(shuffledPlayers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Saves the teams to the database and returns the updated match with the teams.
|
||||
Future<Match> createMatchWithTeams() async {
|
||||
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tallee/data/models/player.dart';
|
||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
|
||||
import 'package:tallee/presentation/widgets/player_selection.dart';
|
||||
|
||||
class EditMembersView extends StatefulWidget {
|
||||
const EditMembersView({
|
||||
super.key,
|
||||
required this.matchPlayer,
|
||||
required this.teamMember,
|
||||
});
|
||||
|
||||
final List<Player> matchPlayer;
|
||||
|
||||
final List<Player> teamMember;
|
||||
|
||||
@override
|
||||
State<EditMembersView> createState() => _EditMembersViewState();
|
||||
}
|
||||
|
||||
class _EditMembersViewState extends State<EditMembersView> {
|
||||
List<Player> selectedPlayers = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
selectedPlayers = [...widget.teamMember];
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(loc.edit_members),
|
||||
leading: HapticIconButton(
|
||||
onPressed: selectedPlayers.isNotEmpty
|
||||
? () => Navigator.pop(context, selectedPlayers)
|
||||
: null,
|
||||
icon: const Icon(Icons.arrow_back_ios_new_outlined),
|
||||
),
|
||||
),
|
||||
body: PlayerSelection(
|
||||
initialSelectedPlayers: widget.teamMember,
|
||||
availablePlayers: widget.matchPlayer,
|
||||
onChanged: (List<Player> newSelectedPlayers) {
|
||||
setState(() {
|
||||
selectedPlayers = newSelectedPlayers;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
import 'dart:core' hide Match;
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttericon/rpg_awesome_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tallee/core/common.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
import 'package:tallee/data/db/database.dart';
|
||||
import 'package:tallee/data/models/match.dart';
|
||||
import 'package:tallee/data/models/team.dart';
|
||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
|
||||
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
||||
import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart';
|
||||
|
||||
/// Displays the given [teams] as a flat reorderable list where every team is
|
||||
/// preceded by a header row and followed by its members. Members can be
|
||||
/// dragged across team boundaries to be reassigned to another team.
|
||||
class ManageMembersView extends StatefulWidget {
|
||||
const ManageMembersView({
|
||||
super.key,
|
||||
required this.match,
|
||||
required this.onWinnerChanged,
|
||||
});
|
||||
|
||||
final Match match;
|
||||
|
||||
final VoidCallback? onWinnerChanged;
|
||||
|
||||
@override
|
||||
State<ManageMembersView> createState() => _ManageMembersViewState();
|
||||
}
|
||||
|
||||
class _ManageMembersViewState extends State<ManageMembersView> {
|
||||
late AppDatabase db;
|
||||
|
||||
late List<Team> teams;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
db = Provider.of<AppDatabase>(context, listen: false);
|
||||
teams = widget.match.teams!;
|
||||
redistributePlayers();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(title: Text(loc.manage_members)),
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ReorderableListView.builder(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
buildDefaultDragHandles: false,
|
||||
itemCount: allItemsCount,
|
||||
onReorder: onReorder,
|
||||
proxyDecorator: (child, index, animation) =>
|
||||
Material(type: MaterialType.transparency, child: child),
|
||||
itemBuilder: (context, index) {
|
||||
final teamIndex = teamIndexForFlat(index);
|
||||
final memberIndex = memberIndexForFlat(index, teamIndex);
|
||||
final team = teams[teamIndex];
|
||||
|
||||
if (memberIndex == -1) {
|
||||
return buildTeamTile(team: team);
|
||||
}
|
||||
|
||||
final player = team.members[memberIndex];
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: ValueKey('player_${player.id}'),
|
||||
index: index,
|
||||
child: TextIconListTile(
|
||||
text: player.name,
|
||||
suffixText: getNameCountText(player),
|
||||
icon: Icons.drag_handle,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: MediaQuery.of(context).padding.bottom,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
MainMenuButton(
|
||||
onPressed: () => setState(() {
|
||||
redistributePlayers();
|
||||
}),
|
||||
icon: Icons.cached,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
MainMenuButton(
|
||||
onPressed: allTeamsHaveMembers
|
||||
? () async => submitMatch()
|
||||
: null,
|
||||
text: loc.create_match,
|
||||
icon: RpgAwesome.clovers_card,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void submitMatch() async {
|
||||
await db.matchDao.addMatch(match: widget.match);
|
||||
if (mounted) {
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MatchResultView(
|
||||
match: widget.match,
|
||||
onWinnerChanged: widget.onWinnerChanged,
|
||||
),
|
||||
),
|
||||
(route) => route.isFirst,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool get allTeamsHaveMembers {
|
||||
return teams.every((team) => team.members.isNotEmpty);
|
||||
}
|
||||
|
||||
// Iterates through all teams and redistributes players randomly and
|
||||
// as evenly as possible.
|
||||
void redistributePlayers() {
|
||||
for (final team in teams) {
|
||||
team.members.clear();
|
||||
}
|
||||
var matchPlayers = widget.match.players;
|
||||
Random random = Random();
|
||||
|
||||
if (matchPlayers.isEmpty || teams.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final shuffledPlayers = [...matchPlayers]..shuffle(random);
|
||||
|
||||
for (int i = 0; i < shuffledPlayers.length; i++) {
|
||||
final teamIndex = i % teams.length;
|
||||
teams[teamIndex].members.add(shuffledPlayers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Total players + teams length
|
||||
int get allItemsCount {
|
||||
var count = 0;
|
||||
for (final team in teams) {
|
||||
count += 1 + team.members.length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Returns the index of the team that owns the flat-list item at [flatIndex].
|
||||
int teamIndexForFlat(int flatIndex) {
|
||||
var remaining = flatIndex;
|
||||
for (var i = 0; i < teams.length; i++) {
|
||||
final size = 1 + teams[i].members.length;
|
||||
if (remaining < size) return i;
|
||||
remaining -= size;
|
||||
}
|
||||
return teams.length - 1;
|
||||
}
|
||||
|
||||
/// Returns the member index within its team, or `-1` if the item at
|
||||
/// [flatIndex] is the team header.
|
||||
int memberIndexForFlat(int flatIndex, int teamIndex) {
|
||||
var offset = 0;
|
||||
for (var i = 0; i < teamIndex; i++) {
|
||||
offset += 1 + teams[i].members.length;
|
||||
}
|
||||
// offset now points to the header of [teamIndex]. Anything beyond is a
|
||||
// member of that team.
|
||||
final localIndex = flatIndex - offset;
|
||||
return localIndex == 0 ? -1 : localIndex - 1;
|
||||
}
|
||||
|
||||
void onReorder(int oldIndex, int newIndex) {
|
||||
final sourceTeamIndex = teamIndexForFlat(oldIndex);
|
||||
final sourceMemberIndex = memberIndexForFlat(oldIndex, sourceTeamIndex);
|
||||
|
||||
// Headers themselves can't be reordered.
|
||||
if (sourceMemberIndex == -1) return;
|
||||
|
||||
// Flutter convention: when moving down, the target index is shifted by 1
|
||||
// because the item is removed first.
|
||||
var targetIndex = newIndex;
|
||||
if (newIndex > oldIndex) targetIndex -= 1;
|
||||
targetIndex = targetIndex.clamp(0, allItemsCount - 1);
|
||||
|
||||
// Resolve target location based on the item currently at [targetIndex]
|
||||
// *before* the move.
|
||||
int destTeamIndex;
|
||||
int insertPositionInTeam;
|
||||
|
||||
if (targetIndex >= allItemsCount - 1 && newIndex >= allItemsCount) {
|
||||
// Dropped at the very end -> append to the last team.
|
||||
destTeamIndex = teams.length - 1;
|
||||
insertPositionInTeam = teams[destTeamIndex].members.length;
|
||||
} else {
|
||||
destTeamIndex = teamIndexForFlat(targetIndex);
|
||||
final anchorMemberIndex = memberIndexForFlat(targetIndex, destTeamIndex);
|
||||
|
||||
if (anchorMemberIndex == -1) {
|
||||
// Dropped right before a header -> append to the previous team.
|
||||
destTeamIndex = destTeamIndex - 1;
|
||||
if (destTeamIndex < 0) {
|
||||
// Dropped above the very first header -> stay in team 0 at top.
|
||||
destTeamIndex = 0;
|
||||
insertPositionInTeam = 0;
|
||||
} else {
|
||||
insertPositionInTeam = teams[destTeamIndex].members.length;
|
||||
}
|
||||
} else {
|
||||
insertPositionInTeam = anchorMemberIndex;
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
final sourceMembers = teams[sourceTeamIndex].members;
|
||||
final player = sourceMembers.removeAt(sourceMemberIndex);
|
||||
|
||||
// Adjust insert index if we removed from before the insert point in the
|
||||
// same team.
|
||||
if (sourceTeamIndex == destTeamIndex &&
|
||||
insertPositionInTeam > sourceMembers.length) {
|
||||
insertPositionInTeam = sourceMembers.length;
|
||||
}
|
||||
|
||||
teams[destTeamIndex].members.insert(insertPositionInTeam, player);
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildTeamTile({required Team team}) {
|
||||
final color = getColorFromGameColor(team.color);
|
||||
return Padding(
|
||||
key: ValueKey('header_${team.id}'),
|
||||
padding: const EdgeInsets.fromLTRB(12, 16, 12, 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 14,
|
||||
height: 14,
|
||||
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
team.name,
|
||||
style: const TextStyle(
|
||||
color: CustomTheme.textColor,
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${team.members.length}',
|
||||
style: const TextStyle(
|
||||
color: CustomTheme.hintColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,34 +3,26 @@ import 'package:tallee/core/common.dart';
|
||||
import 'package:tallee/core/constants.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
import 'package:tallee/core/enums.dart';
|
||||
import 'package:tallee/data/models/player.dart';
|
||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
|
||||
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
|
||||
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
||||
|
||||
class TeamCreationTile extends StatefulWidget {
|
||||
const TeamCreationTile({
|
||||
super.key,
|
||||
required this.color,
|
||||
required this.controller,
|
||||
required this.players,
|
||||
required this.hintText,
|
||||
this.onEdit,
|
||||
this.onDelete,
|
||||
this.onColorSelection,
|
||||
});
|
||||
|
||||
final GameColor color;
|
||||
|
||||
final List<Player> players;
|
||||
|
||||
final TextEditingController controller;
|
||||
|
||||
final String hintText;
|
||||
|
||||
final VoidCallback? onEdit;
|
||||
|
||||
final VoidCallback? onDelete;
|
||||
|
||||
final ValueChanged<GameColor>? onColorSelection;
|
||||
@@ -112,45 +104,6 @@ class _TeamCreationTileState extends State<TeamCreationTile> {
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
loc.players,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: CustomTheme.textColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (widget.players.isEmpty)
|
||||
Text(
|
||||
loc.no_players_selected,
|
||||
style: const TextStyle(color: CustomTheme.hintColor),
|
||||
)
|
||||
else
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: widget.players
|
||||
.map(
|
||||
(player) => TextIconTile(
|
||||
text: player.name,
|
||||
suffixText: getNameCountText(player),
|
||||
iconEnabled: false,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
if (widget.onEdit != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: AnimatedDialogButton(
|
||||
buttonConstraints: const BoxConstraints(
|
||||
minWidth: double.infinity,
|
||||
),
|
||||
buttonText: loc.edit_members,
|
||||
onPressed: widget.onEdit!,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user