Neuer Regelsatz: Platzierung #213
@@ -56,6 +56,7 @@
|
|||||||
"error_deleting_group": "Fehler beim Löschen 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_editing_group": "Fehler beim Bearbeiten der Gruppe, bitte erneut versuchen",
|
||||||
"error_reading_file": "Fehler beim Lesen der Datei",
|
"error_reading_file": "Fehler beim Lesen der Datei",
|
||||||
|
"exit_view": "Ansicht verlassen",
|
||||||
"export_canceled": "Export abgebrochen",
|
"export_canceled": "Export abgebrochen",
|
||||||
"export_data": "Daten exportieren",
|
"export_data": "Daten exportieren",
|
||||||
"format_exception": "Formatfehler (siehe Konsole)",
|
"format_exception": "Formatfehler (siehe Konsole)",
|
||||||
@@ -74,6 +75,7 @@
|
|||||||
"legal": "Rechtliches",
|
"legal": "Rechtliches",
|
||||||
"legal_notice": "Impressum",
|
"legal_notice": "Impressum",
|
||||||
"licenses": "Lizenzen",
|
"licenses": "Lizenzen",
|
||||||
|
"live_edit_mode": "Live-Bearbeitungsmodus",
|
||||||
"match_in_progress": "Spiel läuft...",
|
"match_in_progress": "Spiel läuft...",
|
||||||
"match_name": "Spieltitel",
|
"match_name": "Spieltitel",
|
||||||
"match_profile": "Spielprofil",
|
"match_profile": "Spielprofil",
|
||||||
|
|||||||
@@ -1,352 +1,6 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "en",
|
"@@locale": "en",
|
||||||
|
|
||||||
"@all_players": {
|
|
||||||
"description": "Label for all players list"
|
|
||||||
},
|
|
||||||
"@all_players_selected": {
|
|
||||||
"description": "Message when all players are added to selection"
|
|
||||||
},
|
|
||||||
"@amount_of_matches": {
|
|
||||||
"description": "Label for amount of matches statistic"
|
|
||||||
},
|
|
||||||
"@app_name": {
|
|
||||||
"description": "The name of the App"
|
|
||||||
},
|
|
||||||
"@best_player": {
|
|
||||||
"description": "Label for best player statistic"
|
|
||||||
},
|
|
||||||
"@cancel": {
|
|
||||||
"description": "Cancel button text"
|
|
||||||
},
|
|
||||||
"@choose_game": {
|
|
||||||
"description": "Label for choosing a game"
|
|
||||||
},
|
|
||||||
"@choose_group": {
|
|
||||||
"description": "Label for choosing a group"
|
|
||||||
},
|
|
||||||
"@choose_ruleset": {
|
|
||||||
"description": "Label for choosing a ruleset"
|
|
||||||
},
|
|
||||||
"@could_not_add_player": {
|
|
||||||
"description": "Error message when adding a player fails"
|
|
||||||
},
|
|
||||||
"@create_group": {
|
|
||||||
"description": "Button text to create a group"
|
|
||||||
},
|
|
||||||
"@create_match": {
|
|
||||||
"description": "Button text to create a match"
|
|
||||||
},
|
|
||||||
"@create_new_group": {
|
|
||||||
"description": "Appbar text to create a new group"
|
|
||||||
},
|
|
||||||
"@create_new_match": {
|
|
||||||
"description": "Appbar text to create a new match"
|
|
||||||
},
|
|
||||||
"@created_on": {
|
|
||||||
"description": "Label for creation date"
|
|
||||||
},
|
|
||||||
"@data": {
|
|
||||||
"description": "Data label"
|
|
||||||
},
|
|
||||||
"@data_successfully_deleted": {
|
|
||||||
"description": "Success message after deleting data"
|
|
||||||
},
|
|
||||||
"@data_successfully_exported": {
|
|
||||||
"description": "Success message after exporting data"
|
|
||||||
},
|
|
||||||
"@data_successfully_imported": {
|
|
||||||
"description": "Success message after importing data"
|
|
||||||
},
|
|
||||||
"@days_ago": {
|
|
||||||
"description": "Date format for days ago",
|
|
||||||
"placeholders": {
|
|
||||||
"count": {
|
|
||||||
"type": "int"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@delete": {
|
|
||||||
"description": "Delete button text"
|
|
||||||
},
|
|
||||||
"@delete_all_data": {
|
|
||||||
"description": "Confirmation dialog for deleting all data"
|
|
||||||
},
|
|
||||||
"@delete_group": {
|
|
||||||
"description": "Confirmation dialog for deleting a group"
|
|
||||||
},
|
|
||||||
"@delete_match": {
|
|
||||||
"description": "Button text to delete a match"
|
|
||||||
},
|
|
||||||
"@drag_to_set_placement": {
|
|
||||||
"description": "Label for dragging to set placement"
|
|
||||||
},
|
|
||||||
"@edit_group": {
|
|
||||||
"description": "Button & Appbar label for editing a group"
|
|
||||||
},
|
|
||||||
"@edit_match": {
|
|
||||||
"description": "Button & Appbar label for editing a match"
|
|
||||||
},
|
|
||||||
"@enter_points": {
|
|
||||||
"description": "Label to enter players points"
|
|
||||||
},
|
|
||||||
"@enter_results": {
|
|
||||||
"description": "Button text to enter match results"
|
|
||||||
},
|
|
||||||
"@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"
|
|
||||||
},
|
|
||||||
"@export_canceled": {
|
|
||||||
"description": "Message when export is canceled"
|
|
||||||
},
|
|
||||||
"@export_data": {
|
|
||||||
"description": "Export data menu item"
|
|
||||||
},
|
|
||||||
"@format_exception": {
|
|
||||||
"description": "Error message for format exceptions"
|
|
||||||
},
|
|
||||||
"@game": {
|
|
||||||
"description": "Game label"
|
|
||||||
},
|
|
||||||
"@game_name": {
|
|
||||||
"description": "Placeholder for game name search"
|
|
||||||
},
|
|
||||||
"@group": {
|
|
||||||
"description": "Group label"
|
|
||||||
},
|
|
||||||
"@group_name": {
|
|
||||||
"description": "Placeholder for group name input"
|
|
||||||
},
|
|
||||||
"@group_profile": {
|
|
||||||
"description": "Title for group profile view"
|
|
||||||
},
|
|
||||||
"@groups": {
|
|
||||||
"description": "Label for groups"
|
|
||||||
},
|
|
||||||
"@home": {
|
|
||||||
"description": "Home tab label"
|
|
||||||
},
|
|
||||||
"@import_canceled": {
|
|
||||||
"description": "Message when import is canceled"
|
|
||||||
},
|
|
||||||
"@import_data": {
|
|
||||||
"description": "Import data menu item"
|
|
||||||
},
|
|
||||||
"@info": {
|
|
||||||
"description": "Info label"
|
|
||||||
},
|
|
||||||
"@invalid_schema": {
|
|
||||||
"description": "Error message for invalid schema"
|
|
||||||
},
|
|
||||||
"@least_points": {
|
|
||||||
"description": "Title for least points ruleset"
|
|
||||||
},
|
|
||||||
"@legal": {
|
|
||||||
"description": "Legal section header"
|
|
||||||
},
|
|
||||||
"@legal_notice": {
|
|
||||||
"description": "Legal notice menu item"
|
|
||||||
},
|
|
||||||
"@licenses": {
|
|
||||||
"description": "Licenses menu item"
|
|
||||||
},
|
|
||||||
"@match_in_progress": {
|
|
||||||
"description": "Message when match is in progress"
|
|
||||||
},
|
|
||||||
"@match_name": {
|
|
||||||
"description": "Placeholder for match name input"
|
|
||||||
},
|
|
||||||
"@match_profile": {
|
|
||||||
"description": "Title for match profile view"
|
|
||||||
},
|
|
||||||
"@matches": {
|
|
||||||
"description": "Label for matches"
|
|
||||||
},
|
|
||||||
"@members": {
|
|
||||||
"description": "Label for group members"
|
|
||||||
},
|
|
||||||
"@most_points": {
|
|
||||||
"description": "Title for most points ruleset"
|
|
||||||
},
|
|
||||||
"@no_data_available": {
|
|
||||||
"description": "Message when no data in the statistic tiles is given"
|
|
||||||
},
|
|
||||||
"@no_groups_created_yet": {
|
|
||||||
"description": "Message when no groups exist"
|
|
||||||
},
|
|
||||||
"@no_licenses_found": {
|
|
||||||
"description": "Message when no licenses are found"
|
|
||||||
},
|
|
||||||
"@no_license_text_available": {
|
|
||||||
"description": "Message when no license text is available"
|
|
||||||
},
|
|
||||||
"@no_matches_created_yet": {
|
|
||||||
"description": "Message when no matches exist"
|
|
||||||
},
|
|
||||||
"@no_players_created_yet": {
|
|
||||||
"description": "Message when no players exist"
|
|
||||||
},
|
|
||||||
"@no_players_found_with_that_name": {
|
|
||||||
"description": "Message when search returns no results"
|
|
||||||
},
|
|
||||||
"@no_players_selected": {
|
|
||||||
"description": "Message when no players are selected"
|
|
||||||
},
|
|
||||||
"@no_recent_matches_available": {
|
|
||||||
"description": "Message when no recent matches exist"
|
|
||||||
},
|
|
||||||
"@no_results_entered_yet": {
|
|
||||||
"description": "Message when no results have been entered yet"
|
|
||||||
},
|
|
||||||
"@no_second_match_available": {
|
|
||||||
"description": "Message when no second match exists"
|
|
||||||
},
|
|
||||||
"@no_statistics_available": {
|
|
||||||
"description": "Message when no statistics are available, because no matches were played yet"
|
|
||||||
},
|
|
||||||
"@none": {
|
|
||||||
"description": "None option label"
|
|
||||||
},
|
|
||||||
"@none_group": {
|
|
||||||
"description": "None group option label"
|
|
||||||
},
|
|
||||||
"@not_available": {
|
|
||||||
"description": "Abbreviation for not available"
|
|
||||||
},
|
|
||||||
"@placement": {
|
|
||||||
"description": "Title for placement ruleset"
|
|
||||||
},
|
|
||||||
"@place": {
|
|
||||||
"description": "Label for placement text in match detail view"
|
|
||||||
},
|
|
||||||
"@played_matches": {
|
|
||||||
"description": "Label for played matches statistic"
|
|
||||||
},
|
|
||||||
"@player_name": {
|
|
||||||
"description": "Placeholder for player name input"
|
|
||||||
},
|
|
||||||
"@players": {
|
|
||||||
"description": "Players label"
|
|
||||||
},
|
|
||||||
"@players_count": {
|
|
||||||
"description": "Shows the number of players",
|
|
||||||
"placeholders": {
|
|
||||||
"count": {
|
|
||||||
"type": "int"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@points": {
|
|
||||||
"description": "Points label"
|
|
||||||
},
|
|
||||||
"@privacy_policy": {
|
|
||||||
"description": "Privacy policy menu item"
|
|
||||||
},
|
|
||||||
"@quick_create": {
|
|
||||||
"description": "Title for quick create section"
|
|
||||||
},
|
|
||||||
"@recent_matches": {
|
|
||||||
"description": "Title for recent matches section"
|
|
||||||
},
|
|
||||||
"@results": {
|
|
||||||
"description": "Label for match results"
|
|
||||||
},
|
|
||||||
"@ruleset": {
|
|
||||||
"description": "Ruleset label"
|
|
||||||
},
|
|
||||||
"@ruleset_least_points": {
|
|
||||||
"description": "Description for least points ruleset"
|
|
||||||
},
|
|
||||||
"@ruleset_most_points": {
|
|
||||||
"description": "Description for most points ruleset"
|
|
||||||
},
|
|
||||||
"@ruleset_placement": {
|
|
||||||
"description": "Description for placement ruleset"
|
|
||||||
},
|
|
||||||
"@ruleset_single_loser": {
|
|
||||||
"description": "Description for single loser ruleset"
|
|
||||||
},
|
|
||||||
"@ruleset_single_winner": {
|
|
||||||
"description": "Description for single winner ruleset"
|
|
||||||
},
|
|
||||||
"@save_changes": {
|
|
||||||
"description": "Save changes button text"
|
|
||||||
},
|
|
||||||
"@search_for_groups": {
|
|
||||||
"description": "Hint text for group search input field"
|
|
||||||
},
|
|
||||||
"@search_for_players": {
|
|
||||||
"description": "Hint text for player search input field"
|
|
||||||
},
|
|
||||||
"@select_winner": {
|
|
||||||
"description": "Label to select the winner"
|
|
||||||
},
|
|
||||||
"@select_loser": {
|
|
||||||
"description": "Label to select the loser"
|
|
||||||
},
|
|
||||||
"@selected_players": {
|
|
||||||
"description": "Shows the number of selected players"
|
|
||||||
},
|
|
||||||
"@settings": {
|
|
||||||
"description": "Label for the App Settings"
|
|
||||||
},
|
|
||||||
"@single_loser": {
|
|
||||||
"description": "Title for single loser ruleset"
|
|
||||||
},
|
|
||||||
"@single_winner": {
|
|
||||||
"description": "Title for single winner ruleset"
|
|
||||||
},
|
|
||||||
"@statistics": {
|
|
||||||
"description": "Statistics tab label"
|
|
||||||
},
|
|
||||||
"@stats": {
|
|
||||||
"description": "Stats tab label (short)"
|
|
||||||
},
|
|
||||||
"@successfully_added_player": {
|
|
||||||
"description": "Success message when adding a player",
|
|
||||||
"placeholders": {
|
|
||||||
"playerName": {
|
|
||||||
"type": "String",
|
|
||||||
"example": "John"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@there_is_no_group_matching_your_search": {
|
|
||||||
"description": "Message when search returns no groups"
|
|
||||||
},
|
|
||||||
"@this_cannot_be_undone": {
|
|
||||||
"description": "Warning message for irreversible actions"
|
|
||||||
},
|
|
||||||
"@today_at": {
|
|
||||||
"description": "Date format for today"
|
|
||||||
},
|
|
||||||
"@undo": {
|
|
||||||
"description": "Undo button text"
|
|
||||||
},
|
|
||||||
"@unknown_exception": {
|
|
||||||
"description": "Error message for unknown exceptions"
|
|
||||||
},
|
|
||||||
"@winner": {
|
|
||||||
"description": "Winner label"
|
|
||||||
},
|
|
||||||
"@winrate": {
|
|
||||||
"description": "Label for winrate statistic"
|
|
||||||
},
|
|
||||||
"@wins": {
|
|
||||||
"description": "Label for wins statistic"
|
|
||||||
},
|
|
||||||
"@yesterday_at": {
|
|
||||||
"description": "Date format for yesterday"
|
|
||||||
},
|
|
||||||
"all_players": "All players",
|
"all_players": "All players",
|
||||||
"all_players_selected": "All players selected",
|
"all_players_selected": "All players selected",
|
||||||
"amount_of_matches": "Amount of Matches",
|
"amount_of_matches": "Amount of Matches",
|
||||||
@@ -403,6 +57,7 @@
|
|||||||
"error_deleting_group": "Error while deleting 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_editing_group": "Error while editing group, please try again",
|
||||||
"error_reading_file": "Error reading file",
|
"error_reading_file": "Error reading file",
|
||||||
|
"exit_view": "Exit View",
|
||||||
"export_canceled": "Export canceled",
|
"export_canceled": "Export canceled",
|
||||||
"export_data": "Export data",
|
"export_data": "Export data",
|
||||||
"format_exception": "Format Exception (see console)",
|
"format_exception": "Format Exception (see console)",
|
||||||
@@ -421,6 +76,7 @@
|
|||||||
"legal": "Legal",
|
"legal": "Legal",
|
||||||
"legal_notice": "Legal Notice",
|
"legal_notice": "Legal Notice",
|
||||||
"licenses": "Licenses",
|
"licenses": "Licenses",
|
||||||
|
"live_edit_mode": "Live Edit Mode",
|
||||||
"match_in_progress": "Match in progress...",
|
"match_in_progress": "Match in progress...",
|
||||||
"match_name": "Match name",
|
"match_name": "Match name",
|
||||||
"match_profile": "Match Profile",
|
"match_profile": "Match Profile",
|
||||||
|
|||||||
@@ -392,6 +392,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Error reading file'**
|
/// **'Error reading file'**
|
||||||
String get error_reading_file;
|
String get error_reading_file;
|
||||||
|
|
||||||
|
/// No description provided for @exit_view.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Exit View'**
|
||||||
|
String get exit_view;
|
||||||
|
|
||||||
/// Message when export is canceled
|
/// Message when export is canceled
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -500,6 +506,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Licenses'**
|
/// **'Licenses'**
|
||||||
String get licenses;
|
String get licenses;
|
||||||
|
|
||||||
|
/// No description provided for @live_edit_mode.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Live Edit Mode'**
|
||||||
|
String get live_edit_mode;
|
||||||
|
|
||||||
/// Message when match is in progress
|
/// Message when match is in progress
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get error_reading_file => 'Fehler beim Lesen der Datei';
|
String get error_reading_file => 'Fehler beim Lesen der Datei';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get exit_view => 'Ansicht verlassen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get export_canceled => 'Export abgebrochen';
|
String get export_canceled => 'Export abgebrochen';
|
||||||
|
|
||||||
@@ -225,6 +228,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get licenses => 'Lizenzen';
|
String get licenses => 'Lizenzen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get live_edit_mode => 'Live-Bearbeitungsmodus';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get match_in_progress => 'Spiel läuft...';
|
String get match_in_progress => 'Spiel läuft...';
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get error_reading_file => 'Error reading file';
|
String get error_reading_file => 'Error reading file';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get exit_view => 'Exit View';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get export_canceled => 'Export canceled';
|
String get export_canceled => 'Export canceled';
|
||||||
|
|
||||||
@@ -225,6 +228,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get licenses => 'Licenses';
|
String get licenses => 'Licenses';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get live_edit_mode => 'Live Edit Mode';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get match_in_progress => 'Match in progress...';
|
String get match_in_progress => 'Match in progress...';
|
||||||
|
|
||||||
|
|||||||
@@ -240,6 +240,9 @@ class _MatchDetailViewState extends State<MatchDetailView> {
|
|||||||
match: match,
|
match: match,
|
||||||
onWinnerChanged: () {
|
onWinnerChanged: () {
|
||||||
widget.onMatchUpdate.call();
|
widget.onMatchUpdate.call();
|
||||||
|
setState(() {
|
||||||
|
updateScoresForCurrentMatch();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -428,4 +431,10 @@ class _MatchDetailViewState extends State<MatchDetailView> {
|
|||||||
return '${number}th';
|
return '${number}th';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateScoresForCurrentMatch() {
|
||||||
|
db.scoreEntryDao
|
||||||
|
.getAllMatchScores(matchId: match.id)
|
||||||
|
.then((scores) => match.scores = scores);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import 'package:tallee/data/models/player.dart';
|
|||||||
import 'package:tallee/data/models/score_entry.dart';
|
import 'package:tallee/data/models/score_entry.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
|
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/custom_radio_list_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/score_list_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart';
|
||||||
|
|
||||||
class MatchResultView extends StatefulWidget {
|
class MatchResultView extends StatefulWidget {
|
||||||
@@ -31,6 +32,8 @@ class MatchResultView extends StatefulWidget {
|
|||||||
class _MatchResultViewState extends State<MatchResultView> {
|
class _MatchResultViewState extends State<MatchResultView> {
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
|
|
||||||
|
bool isLiveEditMode = false;
|
||||||
|
|
||||||
late final Ruleset ruleset;
|
late final Ruleset ruleset;
|
||||||
|
|
||||||
/// List of all players who participated in the match
|
/// List of all players who participated in the match
|
||||||
@@ -39,6 +42,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
/// List of text controllers for score entry, one for each player
|
/// List of text controllers for score entry, one for each player
|
||||||
late final List<TextEditingController> controller;
|
late final List<TextEditingController> controller;
|
||||||
|
|
||||||
|
/// Flag to indicate if the save button should be enabled
|
||||||
late bool canSave;
|
late bool canSave;
|
||||||
|
|
||||||
/// Currently selected winner player
|
/// Currently selected winner player
|
||||||
@@ -58,6 +62,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
(index) => TextEditingController()..addListener(() => onTextEnter()),
|
(index) => TextEditingController()..addListener(() => onTextEnter()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Prefill fields
|
||||||
if (widget.match.mvp.isNotEmpty) {
|
if (widget.match.mvp.isNotEmpty) {
|
||||||
if (rulesetSupportsWinnerSelection()) {
|
if (rulesetSupportsWinnerSelection()) {
|
||||||
_selectedPlayer = allPlayers.firstWhere(
|
_selectedPlayer = allPlayers.firstWhere(
|
||||||
@@ -108,186 +113,232 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: isLiveEditMode
|
||||||
margin: const EdgeInsets.symmetric(
|
// Live Edit Mode
|
||||||
horizontal: 12,
|
? ListView.builder(
|
||||||
vertical: 10,
|
itemCount: allPlayers.length,
|
||||||
),
|
itemBuilder: (context, index) {
|
||||||
padding: const EdgeInsets.symmetric(
|
return LiveEditListTile(
|
||||||
vertical: 10,
|
title: allPlayers[index].name,
|
||||||
horizontal: 10,
|
onChanged: (value) {
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: CustomTheme.boxColor,
|
|
||||||
border: Border.all(color: CustomTheme.boxBorderColor),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'${getTitleForRuleset(loc)}:',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
if (rulesetSupportsWinnerSelection())
|
|
||||||
Expanded(
|
|
||||||
child: RadioGroup<Player>(
|
|
||||||
groupValue: _selectedPlayer,
|
|
||||||
onChanged: (Player? value) async {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedPlayer = value;
|
controller[index].text = value.toString();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: ListView.builder(
|
value: int.tryParse(controller[index].text) ?? 0,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
);
|
||||||
itemCount: allPlayers.length,
|
},
|
||||||
itemBuilder: (context, index) {
|
)
|
||||||
return CustomRadioListTile(
|
// Normal Container
|
||||||
text: allPlayers[index].name,
|
: Container(
|
||||||
value: allPlayers[index],
|
margin: const EdgeInsets.symmetric(
|
||||||
onContainerTap: (value) async {
|
horizontal: 12,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10,
|
||||||
|
horizontal: 10,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: CustomTheme.boxColor,
|
||||||
|
border: Border.all(color: CustomTheme.boxBorderColor),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
getTitleForRuleset(loc),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
|
// Show player selection
|
||||||
|
if (rulesetSupportsWinnerSelection())
|
||||||
|
Expanded(
|
||||||
|
child: RadioGroup<Player>(
|
||||||
|
groupValue: _selectedPlayer,
|
||||||
|
onChanged: (Player? value) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
// Check if the already selected player is the same as the newly tapped player.
|
_selectedPlayer = value;
|
||||||
if (_selectedPlayer == value) {
|
|
||||||
// If yes deselected the player by setting it to null.
|
|
||||||
_selectedPlayer = null;
|
|
||||||
} else {
|
|
||||||
// If no assign the newly tapped player to the selected player.
|
|
||||||
(_selectedPlayer = value);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
child: ListView.builder(
|
||||||
},
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
),
|
itemCount: allPlayers.length,
|
||||||
),
|
itemBuilder: (context, index) {
|
||||||
),
|
return CustomRadioListTile(
|
||||||
if (rulesetSupportsScoreEntry())
|
text: allPlayers[index].name,
|
||||||
Expanded(
|
value: allPlayers[index],
|
||||||
child: ListView.separated(
|
onContainerTap: (value) async {
|
||||||
itemCount: allPlayers.length,
|
setState(() {
|
||||||
itemBuilder: (context, index) {
|
// Check if the already selected player is the same as the newly tapped player.
|
||||||
return ScoreListTile(
|
if (_selectedPlayer == value) {
|
||||||
text: allPlayers[index].name,
|
// If yes deselected the player by setting it to null.
|
||||||
controller: controller[index],
|
_selectedPlayer = null;
|
||||||
);
|
} else {
|
||||||
},
|
// If no assign the newly tapped player to the selected player.
|
||||||
separatorBuilder: (BuildContext context, int index) {
|
(_selectedPlayer = value);
|
||||||
return const Padding(
|
}
|
||||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
});
|
||||||
child: Divider(indent: 20),
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (rulesetSupportsPlacement())
|
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
// Placement indicators
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
for (int i = 0; i < allPlayers.length; i++)
|
|
||||||
Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
height: 60,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: CustomTheme.boxBorderColor,
|
|
||||||
borderRadius: CustomTheme
|
|
||||||
.standardBorderRadiusAll,
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
height: 50,
|
|
||||||
width: 50,
|
|
||||||
child: Text(
|
|
||||||
' #${i + 1} ',
|
|
||||||
style: const TextStyle(
|
|
||||||
color: CustomTheme.textColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Drag list
|
// Show score entry
|
||||||
|
if (rulesetSupportsScoreEntry())
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ReorderableListView.builder(
|
child: ListView.separated(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
itemCount: allPlayers.length,
|
||||||
padding: EdgeInsets.zero,
|
itemBuilder: (context, index) {
|
||||||
proxyDecorator: (child, index, animation) {
|
return ScoreListTile(
|
||||||
|
flixcoo marked this conversation as resolved
Outdated
|
|||||||
return AnimatedBuilder(
|
text: allPlayers[index].name,
|
||||||
animation: animation,
|
controller: controller[index],
|
||||||
child: child,
|
);
|
||||||
builder: (context, child) {
|
},
|
||||||
final alpha =
|
separatorBuilder:
|
||||||
(Curves.easeInOut.transform(
|
(BuildContext context, int index) {
|
||||||
animation.value,
|
return const Padding(
|
||||||
) *
|
padding: EdgeInsets.symmetric(
|
||||||
40)
|
vertical: 8.0,
|
||||||
.toInt();
|
),
|
||||||
return Stack(
|
child: Divider(indent: 20),
|
||||||
children: [
|
);
|
||||||
child!,
|
},
|
||||||
Positioned.fill(
|
),
|
||||||
left: 4,
|
),
|
||||||
top: 4,
|
|
||||||
right: 4,
|
// Show draggable placement list
|
||||||
bottom: 4,
|
if (rulesetSupportsPlacement())
|
||||||
child: DecoratedBox(
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// Placement indicators
|
||||||
|
Padding(
|
||||||
|
flixcoo marked this conversation as resolved
Outdated
flixcoo
commented
Gerne hier noch ein kleines Highlighting implementieren, wenn ein Tile gedraggt wird Gerne hier noch ein kleines Highlighting implementieren, wenn ein Tile gedraggt wird
sneeex
commented
so nen highlighting? so nen highlighting?
flixcoo
commented
Das mag ich garnicht. Lieber was simples, z.B. BG color etwas heller, ne weiße border oder so Das mag ich garnicht. Lieber was simples, z.B. BG color etwas heller, ne weiße border oder so
sneeex
commented
dann schau mal selber ich habe wirklich keine ahnung dann schau mal selber ich habe wirklich keine ahnung
|
|||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
for (
|
||||||
|
int i = 0;
|
||||||
|
i < allPlayers.length;
|
||||||
|
i++
|
||||||
|
)
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 60,
|
||||||
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withAlpha(
|
color:
|
||||||
alpha,
|
CustomTheme.boxBorderColor,
|
||||||
),
|
|
||||||
borderRadius: CustomTheme
|
borderRadius: CustomTheme
|
||||||
.standardBorderRadiusAll,
|
.standardBorderRadiusAll,
|
||||||
),
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 50,
|
||||||
|
width: 50,
|
||||||
|
child: Text(
|
||||||
|
' #${i + 1} ',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: CustomTheme.textColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
);
|
|
||||||
},
|
// Drag list
|
||||||
onReorder: (int oldIndex, int newIndex) {
|
Expanded(
|
||||||
setState(() {
|
child: ReorderableListView.builder(
|
||||||
if (newIndex > oldIndex) {
|
physics:
|
||||||
newIndex -= 1;
|
const NeverScrollableScrollPhysics(),
|
||||||
}
|
padding: EdgeInsets.zero,
|
||||||
final Player item = allPlayers.removeAt(
|
proxyDecorator: (child, index, animation) {
|
||||||
oldIndex,
|
return AnimatedBuilder(
|
||||||
);
|
animation: animation,
|
||||||
allPlayers.insert(newIndex, item);
|
child: child,
|
||||||
});
|
builder: (context, child) {
|
||||||
},
|
final alpha =
|
||||||
itemCount: allPlayers.length,
|
(Curves.easeInOut.transform(
|
||||||
itemBuilder: (context, index) {
|
animation.value,
|
||||||
return TextIconListTile(
|
) *
|
||||||
key: ValueKey(allPlayers[index].id),
|
40)
|
||||||
text: allPlayers[index].name,
|
.toInt();
|
||||||
icon: Icons.drag_handle,
|
return Stack(
|
||||||
);
|
children: [
|
||||||
},
|
child!,
|
||||||
|
Positioned.fill(
|
||||||
|
left: 4,
|
||||||
|
top: 4,
|
||||||
|
right: 4,
|
||||||
|
bottom: 4,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white
|
||||||
|
.withAlpha(alpha),
|
||||||
|
borderRadius: CustomTheme
|
||||||
|
.standardBorderRadiusAll,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onReorder: (int oldIndex, int newIndex) {
|
||||||
|
setState(() {
|
||||||
|
if (newIndex > oldIndex) {
|
||||||
|
newIndex -= 1;
|
||||||
|
}
|
||||||
|
final Player item = allPlayers
|
||||||
|
.removeAt(oldIndex);
|
||||||
|
allPlayers.insert(newIndex, item);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
itemCount: allPlayers.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return TextIconListTile(
|
||||||
|
key: ValueKey(allPlayers[index].id),
|
||||||
|
text: allPlayers[index].name,
|
||||||
|
icon: Icons.drag_handle,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
|
if (rulesetSupportsScoreEntry())
|
||||||
|
// Button to switch to live edit mode
|
||||||
|
...[
|
||||||
|
CustomWidthButton(
|
||||||
|
text: isLiveEditMode ? loc.exit_view : loc.live_edit_mode,
|
||||||
|
sizeRelativeToWidth: 0.95,
|
||||||
|
buttonType: ButtonType.secondary,
|
||||||
|
onPressed: () => setState(() {
|
||||||
|
isLiveEditMode = !isLiveEditMode;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
],
|
||||||
|
|
||||||
|
// Save Changes Button
|
||||||
CustomWidthButton(
|
CustomWidthButton(
|
||||||
text: loc.save_changes,
|
text: loc.save_changes,
|
||||||
sizeRelativeToWidth: 0.95,
|
sizeRelativeToWidth: 0.95,
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ const allDependencies = <Package>[
|
|||||||
_flutter,
|
_flutter,
|
||||||
_flutter_lints,
|
_flutter_lints,
|
||||||
_flutter_localizations,
|
_flutter_localizations,
|
||||||
|
_flutter_numeric_text,
|
||||||
_flutter_plugin_android_lifecycle,
|
_flutter_plugin_android_lifecycle,
|
||||||
_flutter_popup,
|
_flutter_popup,
|
||||||
_flutter_test,
|
_flutter_test,
|
||||||
@@ -169,6 +170,7 @@ const dependencies = <Package>[
|
|||||||
_file_saver,
|
_file_saver,
|
||||||
_flutter,
|
_flutter,
|
||||||
_flutter_localizations,
|
_flutter_localizations,
|
||||||
|
_flutter_numeric_text,
|
||||||
_flutter_popup,
|
_flutter_popup,
|
||||||
_fluttericon,
|
_fluttericon,
|
||||||
_font_awesome_flutter,
|
_font_awesome_flutter,
|
||||||
@@ -2591,6 +2593,42 @@ const _flutter_localizations = Package(
|
|||||||
devDependencies: [PackageRef('flutter_test')],
|
devDependencies: [PackageRef('flutter_test')],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// flutter_numeric_text 1.3.3
|
||||||
|
const _flutter_numeric_text = Package(
|
||||||
|
name: 'flutter_numeric_text',
|
||||||
|
description: 'This widget allows you to animate any text. The widget is easy to use and allows you to seamlessly replace Text(data) with NumericText(data).',
|
||||||
|
homepage: 'https://github.com/strash/flutter_numeric_text',
|
||||||
|
repository: 'https://github.com/strash/flutter_numeric_text',
|
||||||
|
authors: [],
|
||||||
|
version: '1.3.3',
|
||||||
|
spdxIdentifiers: ['MIT'],
|
||||||
|
isMarkdown: false,
|
||||||
|
isSdk: false,
|
||||||
|
dependencies: [PackageRef('flutter')],
|
||||||
|
devDependencies: [PackageRef('flutter_test'), PackageRef('flutter_lints')],
|
||||||
|
license: '''MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Strash One
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.''',
|
||||||
|
);
|
||||||
|
|
||||||
/// flutter_plugin_android_lifecycle 2.0.34
|
/// flutter_plugin_android_lifecycle 2.0.34
|
||||||
const _flutter_plugin_android_lifecycle = Package(
|
const _flutter_plugin_android_lifecycle = Package(
|
||||||
name: 'flutter_plugin_android_lifecycle',
|
name: 'flutter_plugin_android_lifecycle',
|
||||||
@@ -37713,16 +37751,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.''',
|
SOFTWARE.''',
|
||||||
);
|
);
|
||||||
|
|
||||||
/// tallee 0.0.28+262
|
/// tallee 0.0.29+263
|
||||||
const _tallee = Package(
|
const _tallee = Package(
|
||||||
name: 'tallee',
|
name: 'tallee',
|
||||||
description: 'Tracking App for Card Games',
|
description: 'Tracking App for Card Games',
|
||||||
authors: [],
|
authors: [],
|
||||||
version: '0.0.28+262',
|
version: '0.0.29+263',
|
||||||
spdxIdentifiers: ['LGPL-3.0'],
|
spdxIdentifiers: ['LGPL-3.0'],
|
||||||
isMarkdown: false,
|
isMarkdown: false,
|
||||||
isSdk: false,
|
isSdk: false,
|
||||||
dependencies: [PackageRef('clock'), PackageRef('collection'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('flutter'), PackageRef('flutter_localizations'), PackageRef('flutter_popup'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')],
|
dependencies: [PackageRef('clock'), PackageRef('collection'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('flutter'), PackageRef('flutter_localizations'), PackageRef('flutter_numeric_text'), PackageRef('flutter_popup'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')],
|
||||||
devDependencies: [PackageRef('flutter_test'), PackageRef('build_runner'), PackageRef('dart_pubspec_licenses'), PackageRef('drift_dev'), PackageRef('flutter_lints')],
|
devDependencies: [PackageRef('flutter_test'), PackageRef('build_runner'), PackageRef('dart_pubspec_licenses'), PackageRef('drift_dev'), PackageRef('flutter_lints')],
|
||||||
license: '''GNU LESSER GENERAL PUBLIC LICENSE
|
license: '''GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class CustomWidthButton extends StatelessWidget {
|
|||||||
MediaQuery.sizeOf(context).width * sizeRelativeToWidth,
|
MediaQuery.sizeOf(context).width * sizeRelativeToWidth,
|
||||||
60,
|
60,
|
||||||
),
|
),
|
||||||
side: BorderSide(color: borderSideColor, width: 2),
|
side: BorderSide(color: borderSideColor, width: 3),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: CustomTheme.standardBorderRadiusAll,
|
borderRadius: CustomTheme.standardBorderRadiusAll,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class MainMenuButton extends StatefulWidget {
|
class MainMenuButton extends StatefulWidget {
|
||||||
@@ -10,6 +12,7 @@ class MainMenuButton extends StatefulWidget {
|
|||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
this.text,
|
this.text,
|
||||||
|
this.onLongPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The callback to be invoked when the button is pressed.
|
/// The callback to be invoked when the button is pressed.
|
||||||
@@ -21,6 +24,8 @@ class MainMenuButton extends StatefulWidget {
|
|||||||
/// The text of the button.
|
/// The text of the button.
|
||||||
final String? text;
|
final String? text;
|
||||||
|
|
||||||
|
final void Function()? onLongPressed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MainMenuButton> createState() => _MainMenuButtonState();
|
State<MainMenuButton> createState() => _MainMenuButtonState();
|
||||||
}
|
}
|
||||||
@@ -30,6 +35,14 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
|||||||
late AnimationController _animationController;
|
late AnimationController _animationController;
|
||||||
late Animation<double> _scaleAnimation;
|
late Animation<double> _scaleAnimation;
|
||||||
|
|
||||||
|
/// How long the button needs to be pressed to register it as long press
|
||||||
|
Timer? _longPressTimer;
|
||||||
|
|
||||||
|
/// How much time between two onLongPressed calls
|
||||||
|
Timer? _repeatTimer;
|
||||||
|
|
||||||
|
bool _isLongPressing = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -51,14 +64,29 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTapDown: (_) {
|
onTapDown: (_) {
|
||||||
_animationController.forward();
|
_animationController.forward();
|
||||||
},
|
if (widget.onLongPressed != null) {
|
||||||
onTapUp: (_) async {
|
_longPressTimer = Timer(const Duration(milliseconds: 400), () {
|
||||||
await _animationController.reverse();
|
_isLongPressing = true;
|
||||||
if (mounted) {
|
widget.onLongPressed?.call();
|
||||||
widget.onPressed();
|
_repeatTimer = Timer.periodic(
|
||||||
|
const Duration(milliseconds: 250),
|
||||||
|
(_) => widget.onLongPressed?.call(),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onTapUp: (_) async {
|
||||||
|
_cancelTimers();
|
||||||
|
if (mounted && !_isLongPressing) {
|
||||||
|
widget.onPressed();
|
||||||
|
}
|
||||||
|
_isLongPressing = false;
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
await _animationController.reverse();
|
||||||
|
},
|
||||||
onTapCancel: () {
|
onTapCancel: () {
|
||||||
|
_isLongPressing = false;
|
||||||
|
_cancelTimers();
|
||||||
_animationController.reverse();
|
_animationController.reverse();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -92,7 +120,15 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_cancelTimers();
|
||||||
_animationController.dispose();
|
_animationController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _cancelTimers() {
|
||||||
|
_longPressTimer?.cancel();
|
||||||
|
_longPressTimer = null;
|
||||||
|
_repeatTimer?.cancel();
|
||||||
|
_repeatTimer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_numeric_text/flutter_numeric_text.dart';
|
||||||
|
import 'package:tallee/core/custom_theme.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
||||||
|
|
||||||
|
class LiveEditListTile extends StatefulWidget {
|
||||||
|
const LiveEditListTile({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.value,
|
||||||
|
this.onChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
final int value;
|
||||||
|
|
||||||
|
final void Function(int newValue)? onChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LiveEditListTile> createState() => _LiveEditListTileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveEditListTileState extends State<LiveEditListTile> {
|
||||||
|
int _score = 0;
|
||||||
|
final int maxScore = 9999;
|
||||||
|
final int minScore = -9999;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_score = widget.value;
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20),
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
|
||||||
|
decoration: CustomTheme.standardBoxDecoration,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
MainMenuButton(
|
||||||
|
onPressed: () => _score > minScore
|
||||||
|
? {
|
||||||
|
setState(() {
|
||||||
|
_score--;
|
||||||
|
if (widget.onChanged != null) {
|
||||||
|
widget.onChanged!(_score);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
onLongPressed: () => _score > minScore
|
||||||
|
? {
|
||||||
|
setState(() {
|
||||||
|
_score -= 10;
|
||||||
|
if (widget.onChanged != null) {
|
||||||
|
widget.onChanged!(_score);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: Icons.remove_rounded,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 150,
|
||||||
|
child: NumericText(
|
||||||
|
_score.toString(),
|
||||||
|
maxLines: 1,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
|
textHeightBehavior: const TextHeightBehavior(
|
||||||
|
applyHeightToFirstAscent: false,
|
||||||
|
applyHeightToLastDescent: false,
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 48,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MainMenuButton(
|
||||||
|
onPressed: () => _score < maxScore
|
||||||
|
? {
|
||||||
|
setState(() {
|
||||||
|
_score++;
|
||||||
|
if (widget.onChanged != null) {
|
||||||
|
widget.onChanged!(_score);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
onLongPressed: () => _score > minScore
|
||||||
|
? {
|
||||||
|
setState(() {
|
||||||
|
_score += 10;
|
||||||
|
if (widget.onChanged != null) {
|
||||||
|
widget.onChanged!(_score);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: Icons.add_rounded,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,9 +40,13 @@ class ScoreListTile extends StatelessWidget {
|
|||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: const TextInputType.numberWithOptions(signed: true),
|
||||||
maxLength: 4,
|
maxLength: 5,
|
||||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
inputFormatters: [
|
||||||
|
TextInputFormatter.withFunction((oldValue, newValue) {
|
||||||
|
return isValidScoreInput(newValue.text) ? newValue : oldValue;
|
||||||
|
}),
|
||||||
|
],
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@@ -62,7 +66,7 @@ class ScoreListTile extends StatelessWidget {
|
|||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: CustomTheme.textColor.withAlpha(100),
|
color: CustomTheme.textColor.withAlpha(250),
|
||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -80,4 +84,21 @@ class ScoreListTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates the input for the score text field.
|
||||||
|
bool isValidScoreInput(String text) {
|
||||||
|
if (text.isEmpty || text == '-') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final isNegative = text.startsWith('-');
|
||||||
|
final digits = isNegative ? text.substring(1) : text;
|
||||||
|
|
||||||
|
if (digits.isEmpty || digits.length > 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHeck if all characters are digits 0 <= x <= 9
|
||||||
|
return digits.codeUnits.every((unit) => unit >= 48 && unit <= 57);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
name: tallee
|
name: tallee
|
||||||
description: "Tracking App for Card Games"
|
description: "Tracking App for Card Games"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.0.28+262
|
version: 0.0.29+263
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.8.1
|
sdk: ^3.8.1
|
||||||
@@ -18,6 +18,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_numeric_text: ^1.3.3
|
||||||
flutter_popup: ^3.3.9
|
flutter_popup: ^3.3.9
|
||||||
fluttericon: ^2.0.0
|
fluttericon: ^2.0.0
|
||||||
font_awesome_flutter: ^11.0.0
|
font_awesome_flutter: ^11.0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user
Vielleicht andere box decoration, maybe keine border und kompletter container in der border color
tote nummer

ja fair, aber irgendwie würd ich die gern anders darstellen. Nicht genau so wie die list tiles. Und macht die mal quadratisch
so?

Mach mal color: orange, fontcolor: weiß
meinst du so?
sonst probier selber mal rum, du weißt ja besser was du willst