From bc997633eba24305d9b6059857c5de507df999e1 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 02:08:40 +0200 Subject: [PATCH 01/20] feat: add placement ruleset and related localization --- lib/core/common.dart | 2 + lib/core/enums.dart | 2 + lib/data/models/match.dart | 3 + lib/l10n/arb/app_de.arb | 2 + lib/l10n/arb/app_en.arb | 12 +++ lib/l10n/generated/app_localizations.dart | 18 ++++ lib/l10n/generated/app_localizations_de.dart | 10 ++ lib/l10n/generated/app_localizations_en.dart | 10 ++ .../match_view/match_detail_view.dart | 17 ++-- .../match_view/match_result_view.dart | 94 +++++++++++++++++++ .../widgets/tiles/text_icon_list_tile.dart | 12 +++ 11 files changed, 176 insertions(+), 6 deletions(-) diff --git a/lib/core/common.dart b/lib/core/common.dart index 8027180..187e3c1 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -18,6 +18,8 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) { return loc.single_loser; case Ruleset.multipleWinners: return loc.multiple_winners; + case Ruleset.placement: + return loc.placement; } } diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 6b33124..605d3aa 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -32,12 +32,14 @@ enum ExportResult { success, canceled, unknownException } /// - [Ruleset.singleWinner]: The match is won by a single player. /// - [Ruleset.singleLoser]: The match has a single loser. /// - [Ruleset.multipleWinners]: Multiple players can be winners. +/// - [Ruleset.placement]: The player with the highest placement wins. enum Ruleset { highestScore, lowestScore, singleWinner, singleLoser, multipleWinners, + placement, } /// Different colors available for games diff --git a/lib/data/models/match.dart b/lib/data/models/match.dart index 9d14bb3..679f8a4 100644 --- a/lib/data/models/match.dart +++ b/lib/data/models/match.dart @@ -156,6 +156,9 @@ class Match { case Ruleset.multipleWinners: return []; + + case Ruleset.placement: + return _getPlayersWithHighestScore().take(1).toList(); } } diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 46c780a..fedd7dc 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -71,6 +71,7 @@ "none": "Kein", "none_group": "Keine", "not_available": "Nicht verfügbar", + "placement": "Platzierung", "played_matches": "Gespielte Spiele", "player_name": "Spieler:innenname", "players": "Spieler:innen", @@ -85,6 +86,7 @@ "ruleset": "Regelwerk", "ruleset_least_points": "Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.", "ruleset_most_points": "Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.", + "ruleset_placement": "Spieler:innen können in einer Reihenfolge angeordnet werden, die ihre Platzierung reflektiert.", "ruleset_single_loser": "Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.", "ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.", "save_changes": "Änderungen speichern", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index a85e1b0..82f7404 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -77,6 +77,9 @@ "@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" }, @@ -218,6 +221,9 @@ "@not_available": { "description": "Abbreviation for not available" }, + "@placement": { + "description": "Title for placement ruleset" + }, "@played_matches": { "description": "Label for played matches statistic" }, @@ -259,6 +265,9 @@ "@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" }, @@ -358,6 +367,7 @@ "delete_all_data": "Delete all data", "delete_group": "Delete Group", "delete_match": "Delete Match", + "drag_to_set_placement": "Drag to set placement", "edit_group": "Edit Group", "edit_match": "Edit Match", "enter_points": "Enter points", @@ -405,6 +415,7 @@ "none": "None", "none_group": "None", "not_available": "Not available", + "placement": "Placement", "played_matches": "Played Matches", "player_name": "Player name", "players": "Players", @@ -418,6 +429,7 @@ "ruleset": "Ruleset", "ruleset_least_points": "Inverse scoring: the player with the fewest points wins.", "ruleset_most_points": "Traditional ruleset: the player with the most points wins.", + "ruleset_placement": "Players can be arranged in an order, which reflects their placement.", "ruleset_single_loser": "Exactly one loser is determined; last place receives the penalty or consequence.", "ruleset_single_winner": "Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.", "save_changes": "Save Changes", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 99c9317..f4cd87c 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -242,6 +242,12 @@ abstract class AppLocalizations { /// **'Delete Match'** String get delete_match; + /// Label for dragging to set placement + /// + /// In en, this message translates to: + /// **'Drag to set placement'** + String get drag_to_set_placement; + /// Button & Appbar label for editing a group /// /// In en, this message translates to: @@ -524,6 +530,12 @@ abstract class AppLocalizations { /// **'Not available'** String get not_available; + /// Title for placement ruleset + /// + /// In en, this message translates to: + /// **'Placement'** + String get placement; + /// Label for played matches statistic /// /// In en, this message translates to: @@ -602,6 +614,12 @@ abstract class AppLocalizations { /// **'Traditional ruleset: the player with the most points wins.'** String get ruleset_most_points; + /// Description for placement ruleset + /// + /// In en, this message translates to: + /// **'Players can be arranged in an order, which reflects their placement.'** + String get ruleset_placement; + /// Description for single loser ruleset /// /// 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 51b4c62..f53261d 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -84,6 +84,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get delete_match => 'Spiel löschen'; + @override + String get drag_to_set_placement => 'Ziehen, um die Platzierung zu setzen'; + @override String get edit_group => 'Gruppe bearbeiten'; @@ -229,6 +232,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get not_available => 'Nicht verfügbar'; + @override + String get placement => 'Platzierung'; + @override String get played_matches => 'Gespielte Spiele'; @@ -272,6 +278,10 @@ class AppLocalizationsDe extends AppLocalizations { String get ruleset_most_points => 'Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.'; + @override + String get ruleset_placement => + 'Spieler:innen können in einer Reihenfolge angeordnet werden, die ihre Platzierung reflektiert.'; + @override String get ruleset_single_loser => 'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 2b42e47..6dcfda1 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -84,6 +84,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get delete_match => 'Delete Match'; + @override + String get drag_to_set_placement => 'Drag to set placement'; + @override String get edit_group => 'Edit Group'; @@ -229,6 +232,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get not_available => 'Not available'; + @override + String get placement => 'Placement'; + @override String get played_matches => 'Played Matches'; @@ -272,6 +278,10 @@ class AppLocalizationsEn extends AppLocalizations { String get ruleset_most_points => 'Traditional ruleset: the player with the most points wins.'; + @override + String get ruleset_placement => + 'Players can be arranged in an order, which reflects their placement.'; + @override String get ruleset_single_loser => 'Exactly one loser is determined; last place receives the penalty or consequence.'; diff --git a/lib/presentation/views/main_menu/match_view/match_detail_view.dart b/lib/presentation/views/main_menu/match_view/match_detail_view.dart index 2117b77..6f09301 100644 --- a/lib/presentation/views/main_menu/match_view/match_detail_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_detail_view.dart @@ -288,34 +288,39 @@ class _MatchDetailViewState extends State { } } - /// Returns the result widget for scores + /// Returns the result widget for scores or placement Widget getScoreResultWidget(AppLocalizations loc) { List<(String, int)> playerScores = []; for (var player in match.players) { int score = match.scores[player.id]?.score ?? 0; playerScores.add((player.name, score)); } - if (widget.match.game.ruleset == Ruleset.highestScore) { + + final ruleset = match.game.ruleset; + + if (ruleset == Ruleset.highestScore || ruleset == Ruleset.placement) { playerScores.sort((a, b) => b.$2.compareTo(a.$2)); - } else if (widget.match.game.ruleset == Ruleset.lowestScore) { + } else if (ruleset == Ruleset.lowestScore) { playerScores.sort((a, b) => a.$2.compareTo(b.$2)); } return Column( children: [ - for (var score in playerScores) + for (var i = 0; i < playerScores.length; i++) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - score.$1, + playerScores[i].$1, style: const TextStyle( fontSize: 16, color: CustomTheme.textColor, ), ), Text( - getPointLabel(loc, score.$2), + ruleset == Ruleset.placement + ? '#${i + 1}' + : getPointLabel(loc, playerScores[i].$2), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 1fd6780..bf85e03 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -10,6 +10,7 @@ import 'package:tallee/l10n/generated/app_localizations.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/score_list_tile.dart'; +import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart'; class MatchResultView extends StatefulWidget { /// A view that allows selecting and saving the winner of a match @@ -68,6 +69,12 @@ class _MatchResultViewState extends State { final score = scoreList?.score ?? 0; controller[i].text = score.toString(); } + } else if (rulesetSupportsPlacement()) { + allPlayers.sort((a, b) { + final scoreA = widget.match.scores[a.id]?.score ?? 0; + final scoreB = widget.match.scores[b.id]?.score ?? 0; + return scoreB.compareTo(scoreA); + }); } super.initState(); } @@ -177,6 +184,70 @@ class _MatchResultViewState extends State { }, ), ), + if (rulesetSupportsPlacement()) + Expanded( + child: Row( + children: [ + 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: + CustomTheme.standardBoxDecoration, + alignment: Alignment.center, + height: 50, + width: 40, + child: Text( + " #${i + 1} ", + style: const TextStyle( + color: CustomTheme.primaryColor, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ), + ], + ), + ), + Expanded( + child: ReorderableListView.builder( + padding: EdgeInsets.zero, + proxyDecorator: (child, index, animation) { + return Material( + color: Colors.transparent, + child: child, + ); + }, + 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, + iconEnabled: false, + ); + }, + ), + ), + ], + ), + ), ], ), ), @@ -222,6 +293,8 @@ class _MatchResultViewState extends State { } else if (ruleset == Ruleset.lowestScore || ruleset == Ruleset.highestScore) { await _handleScores(); + } else if (ruleset == Ruleset.placement) { + await _handlePlacement(); } widget.onWinnerChanged?.call(); @@ -267,12 +340,29 @@ class _MatchResultViewState extends State { } } + /// Handles saving the placement for each player in the database. + Future _handlePlacement() async { + for (int i = 0; i < allPlayers.length; i++) { + await db.scoreEntryDao.addScore( + matchId: widget.match.id, + playerId: allPlayers[i].id, + entry: ScoreEntry( + roundNumber: 0, + score: allPlayers.length - i, + change: 0, + ), + ); + } + } + String getTitleForRuleset(AppLocalizations loc) { switch (ruleset) { case Ruleset.singleWinner: return loc.select_winner; case Ruleset.singleLoser: return loc.select_loser; + case Ruleset.placement: + return loc.drag_to_set_placement; default: return loc.enter_points; } @@ -285,4 +375,8 @@ class _MatchResultViewState extends State { bool rulesetSupportsScoreEntry() { return ruleset == Ruleset.lowestScore || ruleset == Ruleset.highestScore; } + + bool rulesetSupportsPlacement() { + return ruleset == Ruleset.placement; + } } diff --git a/lib/presentation/widgets/tiles/text_icon_list_tile.dart b/lib/presentation/widgets/tiles/text_icon_list_tile.dart index a31f2ae..f77b5c3 100644 --- a/lib/presentation/widgets/tiles/text_icon_list_tile.dart +++ b/lib/presentation/widgets/tiles/text_icon_list_tile.dart @@ -10,6 +10,7 @@ class TextIconListTile extends StatelessWidget { super.key, required this.text, this.suffixText = '', + this.prefixText = '', this.iconEnabled = true, this.onPressed, }); @@ -20,6 +21,9 @@ class TextIconListTile extends StatelessWidget { /// An optional suffix text to display after the main text. final String suffixText; + /// An optional prefix text to display before the main text. + final String prefixText; + /// A boolean to determine if the icon should be displayed. final bool iconEnabled; @@ -44,6 +48,14 @@ class TextIconListTile extends StatelessWidget { text: TextSpan( style: DefaultTextStyle.of(context).style, children: [ + TextSpan( + text: prefixText, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: CustomTheme.primaryColor, + ), + ), TextSpan( text: text, style: const TextStyle( From 868460b0236af71f7c254a42b6a875d4f4e4b9e9 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 11:50:49 +0200 Subject: [PATCH 02/20] feat: update TextIconListTile to support custom icons --- .../main_menu/match_view/match_result_view.dart | 2 +- lib/presentation/widgets/player_selection.dart | 1 + .../widgets/tiles/text_icon_list_tile.dart | 13 +++++-------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index bf85e03..122f4ec 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -240,7 +240,7 @@ class _MatchResultViewState extends State { return TextIconListTile( key: ValueKey(allPlayers[index].id), text: allPlayers[index].name, - iconEnabled: false, + icon: Icons.drag_handle, ); }, ), diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 0fc8ea0..cdcc2ed 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -196,6 +196,7 @@ class _PlayerSelectionState extends State { return TextIconListTile( text: suggestedPlayers[index].name, suffixText: getNameCountText(suggestedPlayers[index]), + icon: Icons.add, onPressed: () { setState(() { // If the player is not already selected diff --git a/lib/presentation/widgets/tiles/text_icon_list_tile.dart b/lib/presentation/widgets/tiles/text_icon_list_tile.dart index f77b5c3..4c0d648 100644 --- a/lib/presentation/widgets/tiles/text_icon_list_tile.dart +++ b/lib/presentation/widgets/tiles/text_icon_list_tile.dart @@ -11,7 +11,7 @@ class TextIconListTile extends StatelessWidget { required this.text, this.suffixText = '', this.prefixText = '', - this.iconEnabled = true, + this.icon, this.onPressed, }); @@ -24,8 +24,8 @@ class TextIconListTile extends StatelessWidget { /// An optional prefix text to display before the main text. final String prefixText; - /// A boolean to determine if the icon should be displayed. - final bool iconEnabled; + /// The icon to display in the tile. + final IconData? icon; /// The callback to be invoked when the icon is pressed. final VoidCallback? onPressed; @@ -76,11 +76,8 @@ class TextIconListTile extends StatelessWidget { ), ), ), - if (iconEnabled) - GestureDetector( - onTap: onPressed, - child: const Icon(Icons.add, size: 20), - ), + if (icon != null) + GestureDetector(onTap: onPressed, child: Icon(icon, size: 20)), ], ), ); From 8b7a519e645f2672108a31c3afdd62e7ea411212 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 11:51:13 +0200 Subject: [PATCH 03/20] fix: update string interpolation and use const TextStyle for consistency --- .../views/main_menu/match_view/match_result_view.dart | 2 +- lib/presentation/widgets/tiles/text_icon_list_tile.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 122f4ec..bc825c0 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -203,7 +203,7 @@ class _MatchResultViewState extends State { height: 50, width: 40, child: Text( - " #${i + 1} ", + ' #${i + 1} ', style: const TextStyle( color: CustomTheme.primaryColor, fontWeight: FontWeight.bold, diff --git a/lib/presentation/widgets/tiles/text_icon_list_tile.dart b/lib/presentation/widgets/tiles/text_icon_list_tile.dart index 4c0d648..924a164 100644 --- a/lib/presentation/widgets/tiles/text_icon_list_tile.dart +++ b/lib/presentation/widgets/tiles/text_icon_list_tile.dart @@ -50,7 +50,7 @@ class TextIconListTile extends StatelessWidget { children: [ TextSpan( text: prefixText, - style: TextStyle( + style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: CustomTheme.primaryColor, From 6f155182b518f8591c2bff0b51c2267296f5df2b Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 12:33:54 +0200 Subject: [PATCH 04/20] remove scroll-physics from single winner & placement ruleset listview --- lib/l10n/generated/app_localizations_de.dart | 2 +- .../views/main_menu/match_view/match_result_view.dart | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index f53261d..383c3f4 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -85,7 +85,7 @@ class AppLocalizationsDe extends AppLocalizations { String get delete_match => 'Spiel löschen'; @override - String get drag_to_set_placement => 'Ziehen, um die Platzierung zu setzen'; + String get drag_to_set_placement => 'Drag to set placement'; @override String get edit_group => 'Gruppe bearbeiten'; diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index bc825c0..c0e250a 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -144,6 +144,7 @@ class _MatchResultViewState extends State { }); }, child: ListView.builder( + physics: NeverScrollableScrollPhysics(), itemCount: allPlayers.length, itemBuilder: (context, index) { return CustomRadioListTile( @@ -217,6 +218,7 @@ class _MatchResultViewState extends State { ), Expanded( child: ReorderableListView.builder( + physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, proxyDecorator: (child, index, animation) { return Material( From f0062dd9d9b9a57bcb4f10c04dcd00d1290107e4 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 12:34:07 +0200 Subject: [PATCH 05/20] add const --- .../views/main_menu/match_view/match_result_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index c0e250a..7d5ed92 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -144,7 +144,7 @@ class _MatchResultViewState extends State { }); }, child: ListView.builder( - physics: NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), itemCount: allPlayers.length, itemBuilder: (context, index) { return CustomRadioListTile( From 350c5430a4bb607009c694748871ea6ffe4f01a4 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 12:57:42 +0200 Subject: [PATCH 06/20] implement updateMatchStateAfterSave to refresh match scores --- .../main_menu/match_view/match_detail_view.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/presentation/views/main_menu/match_view/match_detail_view.dart b/lib/presentation/views/main_menu/match_view/match_detail_view.dart index 6f09301..3b52505 100644 --- a/lib/presentation/views/main_menu/match_view/match_detail_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_detail_view.dart @@ -205,7 +205,7 @@ class _MatchDetailViewState extends State { match: match, onWinnerChanged: () { widget.onMatchUpdate.call(); - setState(() {}); + updateMatchStateAfterSave(); }, ), ), @@ -230,6 +230,17 @@ class _MatchDetailViewState extends State { widget.onMatchUpdate.call(); } + /// Updates the match scores after saving in MatchResultView + Future updateMatchStateAfterSave() async { + final scores = await db.scoreEntryDao.getAllMatchScores(matchId: match.id); + + if (!mounted) return; + + setState(() { + match.scores = scores; + }); + } + /// Returns the widget to be displayed in the result [InfoTile] Widget getResultWidget(AppLocalizations loc) { if (isSingleRowResult()) { From 2fdcc3e8aa218ecbeba96f4c6adef9b3a55cfc20 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 13:01:42 +0200 Subject: [PATCH 07/20] remove unecessessary prefix text --- .../widgets/tiles/text_icon_list_tile.dart | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/presentation/widgets/tiles/text_icon_list_tile.dart b/lib/presentation/widgets/tiles/text_icon_list_tile.dart index 924a164..04a0803 100644 --- a/lib/presentation/widgets/tiles/text_icon_list_tile.dart +++ b/lib/presentation/widgets/tiles/text_icon_list_tile.dart @@ -10,7 +10,6 @@ class TextIconListTile extends StatelessWidget { super.key, required this.text, this.suffixText = '', - this.prefixText = '', this.icon, this.onPressed, }); @@ -21,9 +20,6 @@ class TextIconListTile extends StatelessWidget { /// An optional suffix text to display after the main text. final String suffixText; - /// An optional prefix text to display before the main text. - final String prefixText; - /// The icon to display in the tile. final IconData? icon; @@ -48,14 +44,6 @@ class TextIconListTile extends StatelessWidget { text: TextSpan( style: DefaultTextStyle.of(context).style, children: [ - TextSpan( - text: prefixText, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: CustomTheme.primaryColor, - ), - ), TextSpan( text: text, style: const TextStyle( From 5b668d28b79e4ee7d0dc20f8de833d24cccfc4c3 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 13:02:42 +0200 Subject: [PATCH 08/20] add translation for drag to set placement --- lib/l10n/arb/app_de.arb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index fedd7dc..a09ef91 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -24,6 +24,7 @@ "delete_all_data": "Alle Daten löschen", "delete_group": "Gruppe löschen", "delete_match": "Spiel löschen", + "drag_to_set_placement": "Ziehen um Platzierung zu setzen", "edit_group": "Gruppe bearbeiten", "edit_match": "Gruppe bearbeiten", "enter_points": "Punkte eingeben", From 2d2a83ea4cb2e6420acdd6d6c48364db1bce652f Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 17:14:38 +0200 Subject: [PATCH 09/20] remove matchStateUpdate bcs alr implement in another pr --- .../main_menu/match_view/match_detail_view.dart | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/match_detail_view.dart b/lib/presentation/views/main_menu/match_view/match_detail_view.dart index 3b52505..ed02538 100644 --- a/lib/presentation/views/main_menu/match_view/match_detail_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_detail_view.dart @@ -205,7 +205,6 @@ class _MatchDetailViewState extends State { match: match, onWinnerChanged: () { widget.onMatchUpdate.call(); - updateMatchStateAfterSave(); }, ), ), @@ -230,17 +229,6 @@ class _MatchDetailViewState extends State { widget.onMatchUpdate.call(); } - /// Updates the match scores after saving in MatchResultView - Future updateMatchStateAfterSave() async { - final scores = await db.scoreEntryDao.getAllMatchScores(matchId: match.id); - - if (!mounted) return; - - setState(() { - match.scores = scores; - }); - } - /// Returns the widget to be displayed in the result [InfoTile] Widget getResultWidget(AppLocalizations loc) { if (isSingleRowResult()) { From 385bd39aa1ba6cf2e41533dc4c3aa42ad0ee3854 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 17:30:44 +0200 Subject: [PATCH 10/20] change placement tile decoration --- .../main_menu/match_view/match_result_view.dart | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 7d5ed92..33c319d 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -198,11 +198,17 @@ class _MatchResultViewState extends State { alignment: Alignment.center, height: 60, child: Container( - decoration: - CustomTheme.standardBoxDecoration, + decoration: BoxDecoration( + color: CustomTheme.boxColor, + border: Border.all( + color: CustomTheme.primaryColor, + ), + borderRadius: CustomTheme + .standardBorderRadiusAll, + ), alignment: Alignment.center, height: 50, - width: 40, + width: 41, child: Text( ' #${i + 1} ', style: const TextStyle( @@ -223,7 +229,8 @@ class _MatchResultViewState extends State { proxyDecorator: (child, index, animation) { return Material( color: Colors.transparent, - child: child, + elevation: 8, + child: Opacity(opacity: 0.9, child: child), ); }, onReorder: (int oldIndex, int newIndex) { From 9781a20b385a944cf7b79bf4cc57c59e80221acd Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 17:36:18 +0200 Subject: [PATCH 11/20] implement dragging animation --- .../match_view/match_result_view.dart | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 33c319d..f0c6fb4 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -227,10 +227,42 @@ class _MatchResultViewState extends State { physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, proxyDecorator: (child, index, animation) { - return Material( - color: Colors.transparent, - elevation: 8, - child: Opacity(opacity: 0.9, child: child), + return AnimatedBuilder( + animation: animation, + builder: (context, child) { + final t = Curves.easeInOut.transform( + animation.value, + ); + return Opacity( + opacity: t, + child: Material( + color: Colors.transparent, + child: Container( + decoration: BoxDecoration( + borderRadius: CustomTheme + .standardBorderRadiusAll, + boxShadow: [ + BoxShadow( + color: CustomTheme + .primaryColor + .withAlpha(30), + blurRadius: 4, + ), + BoxShadow( + color: CustomTheme + .primaryColor + .withAlpha(18), + blurRadius: 12, + spreadRadius: 2, + ), + ], + ), + child: child, + ), + ), + ); + }, + child: child, ); }, onReorder: (int oldIndex, int newIndex) { From 616c23937551d54ad0c270abc10fff3aceaa143f Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 17:47:23 +0200 Subject: [PATCH 12/20] change placement to text in match detail view --- lib/l10n/arb/app_de.arb | 1 + lib/l10n/arb/app_en.arb | 4 +++ lib/l10n/generated/app_localizations.dart | 6 ++++ lib/l10n/generated/app_localizations_de.dart | 5 ++- lib/l10n/generated/app_localizations_en.dart | 3 ++ .../match_view/match_detail_view.dart | 33 ++++++++++++++++++- 6 files changed, 50 insertions(+), 2 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index a09ef91..bdb1da2 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -73,6 +73,7 @@ "none_group": "Keine", "not_available": "Nicht verfügbar", "placement": "Platzierung", + "place": "Platz", "played_matches": "Gespielte Spiele", "player_name": "Spieler:innenname", "players": "Spieler:innen", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 82f7404..a987a38 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -224,6 +224,9 @@ "@placement": { "description": "Title for placement ruleset" }, + "@place": { + "description": "Label for placement text in match detail view" + }, "@played_matches": { "description": "Label for played matches statistic" }, @@ -416,6 +419,7 @@ "none_group": "None", "not_available": "Not available", "placement": "Placement", + "place": "place", "played_matches": "Played Matches", "player_name": "Player name", "players": "Players", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index f4cd87c..481d5ea 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -536,6 +536,12 @@ abstract class AppLocalizations { /// **'Placement'** String get placement; + /// Label for placement text in match detail view + /// + /// In en, this message translates to: + /// **'place'** + String get place; + /// Label for played matches statistic /// /// 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 383c3f4..aa67755 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -85,7 +85,7 @@ class AppLocalizationsDe extends AppLocalizations { String get delete_match => 'Spiel löschen'; @override - String get drag_to_set_placement => 'Drag to set placement'; + String get drag_to_set_placement => 'Ziehen um Platzierung zu setzen'; @override String get edit_group => 'Gruppe bearbeiten'; @@ -235,6 +235,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get placement => 'Platzierung'; + @override + String get place => 'Platz'; + @override String get played_matches => 'Gespielte Spiele'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 6dcfda1..180600c 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -235,6 +235,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get placement => 'Placement'; + @override + String get place => 'place'; + @override String get played_matches => 'Played Matches'; diff --git a/lib/presentation/views/main_menu/match_view/match_detail_view.dart b/lib/presentation/views/main_menu/match_view/match_detail_view.dart index ed02538..2dc9756 100644 --- a/lib/presentation/views/main_menu/match_view/match_detail_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_detail_view.dart @@ -318,7 +318,7 @@ class _MatchDetailViewState extends State { ), Text( ruleset == Ruleset.placement - ? '#${i + 1}' + ? getPlacementText(i + 1, context, loc) : getPointLabel(loc, playerScores[i].$2), style: const TextStyle( fontSize: 16, @@ -337,4 +337,35 @@ class _MatchDetailViewState extends State { return match.game.ruleset == Ruleset.singleWinner || match.game.ruleset == Ruleset.singleLoser; } + + String getPlacementText( + int rank, + BuildContext context, + AppLocalizations loc, + ) { + final locale = Localizations.localeOf(context).languageCode; + + if (locale == 'de') { + return '$rank. ${loc.place}'; + } + + return '${_ordinalEn(rank)} ${loc.place}'; + } + + String _ordinalEn(int number) { + if (number % 100 >= 11 && number % 100 <= 13) { + return '${number}th'; + } + + switch (number % 10) { + case 1: + return '${number}st'; + case 2: + return '${number}nd'; + case 3: + return '${number}rd'; + default: + return '${number}th'; + } + } } From 79ce3efd0ab8d87bf7eac64f9c86862e65fa3a30 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 18:19:30 +0200 Subject: [PATCH 13/20] implement setPlacement in Score Dao & add placement game type to match tile --- lib/data/dao/score_entry_dao.dart | 15 +++++++++++++++ .../match_view/match_result_view.dart | 19 ++++++------------- .../widgets/tiles/match_tile.dart | 3 ++- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/data/dao/score_entry_dao.dart b/lib/data/dao/score_entry_dao.dart index 9c4e01d..cf6a449 100644 --- a/lib/data/dao/score_entry_dao.dart +++ b/lib/data/dao/score_entry_dao.dart @@ -353,4 +353,19 @@ class ScoreEntryDao extends DatabaseAccessor return await deleteAllScoresForMatch(matchId: matchId); } } + + /// Sets the placement for each player in a match. + /// The highest score is assigned to the first player, the second highest to the second player, and so on. + Future setPlacements({ + required String matchId, + required List players, + }) async { + for (int i = 0; i < players.length; i++) { + await db.scoreEntryDao.addScore( + matchId: matchId, + playerId: players[i].id, + entry: ScoreEntry(roundNumber: 0, score: players.length - i, change: 0), + ); + } + } } diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index f0c6fb4..839d109 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -199,7 +199,7 @@ class _MatchResultViewState extends State { height: 60, child: Container( decoration: BoxDecoration( - color: CustomTheme.boxColor, + color: CustomTheme.primaryColor, border: Border.all( color: CustomTheme.primaryColor, ), @@ -212,7 +212,7 @@ class _MatchResultViewState extends State { child: Text( ' #${i + 1} ', style: const TextStyle( - color: CustomTheme.primaryColor, + color: CustomTheme.textColor, fontWeight: FontWeight.bold, fontSize: 16, ), @@ -383,17 +383,10 @@ class _MatchResultViewState extends State { /// Handles saving the placement for each player in the database. Future _handlePlacement() async { - for (int i = 0; i < allPlayers.length; i++) { - await db.scoreEntryDao.addScore( - matchId: widget.match.id, - playerId: allPlayers[i].id, - entry: ScoreEntry( - roundNumber: 0, - score: allPlayers.length - i, - change: 0, - ), - ); - } + await db.scoreEntryDao.setPlacements( + matchId: widget.match.id, + players: allPlayers, + ); } String getTitleForRuleset(AppLocalizations loc) { diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index f7585d6..1149a67 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -303,8 +303,9 @@ class _MatchTileState extends State { final mvp = widget.match.mvp; final mvpScore = widget.match.scores[mvp.first.id]?.score ?? 0; final mvpNames = mvp.map((player) => player.name).join(', '); - return '${loc.winner}: $mvpNames (${getPointLabel(loc, mvpScore)})'; + } else if (ruleset == Ruleset.placement) { + return '${loc.winner}: ${widget.match.mvp.first.name}'; } return '${loc.winner}: n.A.'; } From 496d411af625340cdda0691adddaf2fa4a9ae7bb Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 9 May 2026 18:37:02 +0200 Subject: [PATCH 14/20] feat: updated drag effect --- .../match_view/match_result_view.dart | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 839d109..1c907a4 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -229,40 +229,35 @@ class _MatchResultViewState extends State { proxyDecorator: (child, index, animation) { return AnimatedBuilder( animation: animation, + child: child, builder: (context, child) { - final t = Curves.easeInOut.transform( - animation.value, - ); - return Opacity( - opacity: t, - child: Material( - color: Colors.transparent, - child: Container( - decoration: BoxDecoration( - borderRadius: CustomTheme - .standardBorderRadiusAll, - boxShadow: [ - BoxShadow( - color: CustomTheme - .primaryColor - .withAlpha(30), - blurRadius: 4, + final alpha = + (Curves.easeInOut.transform( + animation.value, + ) * + 40) + .toInt(); + return Stack( + children: [ + child!, + Positioned.fill( + left: 4, + top: 4, + right: 4, + bottom: 4, + child: DecoratedBox( + decoration: BoxDecoration( + color: Colors.white.withAlpha( + alpha, ), - BoxShadow( - color: CustomTheme - .primaryColor - .withAlpha(18), - blurRadius: 12, - spreadRadius: 2, - ), - ], + borderRadius: CustomTheme + .standardBorderRadiusAll, + ), ), - child: child, ), - ), + ], ); }, - child: child, ); }, onReorder: (int oldIndex, int newIndex) { From 1de0ef52ad75d0051be4d2fe20c63e3c4138ad5d Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 9 May 2026 18:41:03 +0200 Subject: [PATCH 15/20] feat: updated placement num styling --- .../views/main_menu/match_view/match_result_view.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 1c907a4..06e0da5 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -189,6 +189,7 @@ class _MatchResultViewState extends State { Expanded( child: Row( children: [ + // Placement indicators Padding( padding: const EdgeInsets.only(right: 8.0), child: Column( @@ -199,16 +200,13 @@ class _MatchResultViewState extends State { height: 60, child: Container( decoration: BoxDecoration( - color: CustomTheme.primaryColor, - border: Border.all( - color: CustomTheme.primaryColor, - ), + color: CustomTheme.boxBorderColor, borderRadius: CustomTheme .standardBorderRadiusAll, ), alignment: Alignment.center, height: 50, - width: 41, + width: 50, child: Text( ' #${i + 1} ', style: const TextStyle( @@ -222,6 +220,8 @@ class _MatchResultViewState extends State { ], ), ), + + // Drag list Expanded( child: ReorderableListView.builder( physics: const NeverScrollableScrollPhysics(), From 3923e955fdd0e74ba8d10afa4e386d0e586b2fa3 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 19:19:18 +0200 Subject: [PATCH 16/20] regenerate localization --- lib/l10n/generated/app_localizations.dart | 234 ++++++++++--------- lib/l10n/generated/app_localizations_de.dart | 15 +- lib/l10n/generated/app_localizations_en.dart | 15 +- 3 files changed, 157 insertions(+), 107 deletions(-) diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 1f6abff..fccffa1 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -98,37 +98,37 @@ abstract class AppLocalizations { Locale('en'), ]; - /// No description provided for @all_players. + /// Label for all players list /// /// In en, this message translates to: /// **'All players'** String get all_players; - /// No description provided for @all_players_selected. + /// Message when all players are added to selection /// /// In en, this message translates to: /// **'All players selected'** String get all_players_selected; - /// No description provided for @amount_of_matches. + /// Label for amount of matches statistic /// /// In en, this message translates to: /// **'Amount of Matches'** String get amount_of_matches; - /// No description provided for @app_name. + /// The name of the App /// /// In en, this message translates to: /// **'Tallee'** String get app_name; - /// No description provided for @best_player. + /// Label for best player statistic /// /// In en, this message translates to: /// **'Best Player'** String get best_player; - /// No description provided for @cancel. + /// Cancel button text /// /// In en, this message translates to: /// **'Cancel'** @@ -140,19 +140,19 @@ abstract class AppLocalizations { /// **'Choose Color'** String get choose_color; - /// No description provided for @choose_game. + /// Label for choosing a game /// /// In en, this message translates to: /// **'Choose Game'** String get choose_game; - /// No description provided for @choose_group. + /// Label for choosing a group /// /// In en, this message translates to: /// **'Choose Group'** String get choose_group; - /// No description provided for @choose_ruleset. + /// Label for choosing a ruleset /// /// In en, this message translates to: /// **'Choose Ruleset'** @@ -212,7 +212,7 @@ abstract class AppLocalizations { /// **'Yellow'** String get color_yellow; - /// No description provided for @could_not_add_player. + /// Error message when adding a player fails /// /// In en, this message translates to: /// **'Could not add player'** @@ -224,73 +224,73 @@ abstract class AppLocalizations { /// **'Create Game'** String get create_game; - /// No description provided for @create_group. + /// Button text to create a group /// /// In en, this message translates to: /// **'Create Group'** String get create_group; - /// No description provided for @create_match. + /// Button text to create a match /// /// In en, this message translates to: /// **'Create match'** String get create_match; - /// No description provided for @create_new_group. + /// Appbar text to create a new group /// /// In en, this message translates to: /// **'Create new group'** String get create_new_group; - /// No description provided for @created_on. + /// Label for creation date /// /// In en, this message translates to: /// **'Created on'** String get created_on; - /// No description provided for @create_new_match. + /// Appbar text to create a new match /// /// In en, this message translates to: /// **'Create new match'** String get create_new_match; - /// No description provided for @data. + /// Data label /// /// In en, this message translates to: /// **'Data'** String get data; - /// No description provided for @data_successfully_deleted. + /// Success message after deleting data /// /// In en, this message translates to: /// **'Data successfully deleted'** String get data_successfully_deleted; - /// No description provided for @data_successfully_exported. + /// Success message after exporting data /// /// In en, this message translates to: /// **'Data successfully exported'** String get data_successfully_exported; - /// No description provided for @data_successfully_imported. + /// Success message after importing data /// /// In en, this message translates to: /// **'Data successfully imported'** String get data_successfully_imported; - /// No description provided for @days_ago. + /// Date format for days ago /// /// In en, this message translates to: /// **'{count} days ago'** - String days_ago(Object count); + String days_ago(int count); - /// No description provided for @delete. + /// Delete button text /// /// In en, this message translates to: /// **'Delete'** String get delete; - /// No description provided for @delete_all_data. + /// Confirmation dialog for deleting all data /// /// In en, this message translates to: /// **'Delete all data'** @@ -308,18 +308,24 @@ abstract class AppLocalizations { /// **'If you delete this game template, {count, plural, =1{1 match} other{{count} matches}} using this game template will also be deleted.'** String delete_game_with_matches_warning(int count); - /// No description provided for @delete_group. + /// Confirmation dialog for deleting a group /// /// In en, this message translates to: /// **'Delete Group'** String get delete_group; - /// No description provided for @delete_match. + /// Button text to delete a match /// /// In en, this message translates to: /// **'Delete Match'** String get delete_match; + /// Label for dragging 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: @@ -332,31 +338,31 @@ abstract class AppLocalizations { /// **'Edit Game'** String get edit_game; - /// No description provided for @edit_group. + /// Button & Appbar label for editing a group /// /// In en, this message translates to: /// **'Edit Group'** String get edit_group; - /// No description provided for @edit_match. + /// Button & Appbar label for editing a match /// /// In en, this message translates to: /// **'Edit Match'** String get edit_match; - /// No description provided for @enter_points. + /// Label to enter players points /// /// In en, this message translates to: /// **'Enter points'** String get enter_points; - /// No description provided for @enter_results. + /// Button text to enter match results /// /// In en, this message translates to: /// **'Enter Results'** String get enter_results; - /// No description provided for @error_creating_group. + /// Error message when group creation fails /// /// In en, this message translates to: /// **'Error while creating group, please try again'** @@ -368,169 +374,169 @@ abstract class AppLocalizations { /// **'Error while deleting game, please try again'** String get error_deleting_game; - /// No description provided for @error_deleting_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; - /// No description provided for @error_editing_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; - /// No description provided for @error_reading_file. + /// Error message when file cannot be read /// /// In en, this message translates to: /// **'Error reading file'** String get error_reading_file; - /// No description provided for @export_canceled. + /// Message when export is canceled /// /// In en, this message translates to: /// **'Export canceled'** String get export_canceled; - /// No description provided for @export_data. + /// Export data menu item /// /// In en, this message translates to: /// **'Export data'** String get export_data; - /// No description provided for @format_exception. + /// Error message for format exceptions /// /// In en, this message translates to: /// **'Format Exception (see console)'** String get format_exception; - /// No description provided for @game. + /// Game label /// /// In en, this message translates to: /// **'Game'** String get game; - /// No description provided for @game_name. + /// Placeholder for game name search /// /// In en, this message translates to: /// **'Game Name'** String get game_name; - /// No description provided for @group. + /// Group label /// /// In en, this message translates to: /// **'Group'** String get group; - /// No description provided for @group_name. + /// Placeholder for group name input /// /// In en, this message translates to: /// **'Group name'** String get group_name; - /// No description provided for @group_profile. + /// Title for group profile view /// /// In en, this message translates to: /// **'Group Profile'** String get group_profile; - /// No description provided for @groups. + /// Label for groups /// /// In en, this message translates to: /// **'Groups'** String get groups; - /// No description provided for @home. + /// Home tab label /// /// In en, this message translates to: /// **'Home'** String get home; - /// No description provided for @import_canceled. + /// Message when import is canceled /// /// In en, this message translates to: /// **'Import canceled'** String get import_canceled; - /// No description provided for @import_data. + /// Import data menu item /// /// In en, this message translates to: /// **'Import data'** String get import_data; - /// No description provided for @info. + /// Info label /// /// In en, this message translates to: /// **'Info'** String get info; - /// No description provided for @invalid_schema. + /// Error message for invalid schema /// /// In en, this message translates to: /// **'Invalid Schema'** String get invalid_schema; - /// No description provided for @least_points. + /// Title for least points ruleset /// /// In en, this message translates to: /// **'Least Points'** String get least_points; - /// No description provided for @legal. + /// Legal section header /// /// In en, this message translates to: /// **'Legal'** String get legal; - /// No description provided for @legal_notice. + /// Legal notice menu item /// /// In en, this message translates to: /// **'Legal Notice'** String get legal_notice; - /// No description provided for @licenses. + /// Licenses menu item /// /// In en, this message translates to: /// **'Licenses'** String get licenses; - /// No description provided for @match_in_progress. + /// Message when match is in progress /// /// In en, this message translates to: /// **'Match in progress...'** String get match_in_progress; - /// No description provided for @match_name. + /// Placeholder for match name input /// /// In en, this message translates to: /// **'Match name'** String get match_name; - /// No description provided for @match_profile. + /// Title for match profile view /// /// In en, this message translates to: /// **'Match Profile'** String get match_profile; - /// No description provided for @matches. + /// Label for matches /// /// In en, this message translates to: /// **'Matches'** String get matches; - /// No description provided for @members. + /// Label for group members /// /// In en, this message translates to: /// **'Members'** String get members; - /// No description provided for @most_points. + /// Title for most points ruleset /// /// In en, this message translates to: /// **'Most Points'** String get most_points; - /// No description provided for @no_data_available. + /// Message when no data in the statistic tiles is given /// /// In en, this message translates to: /// **'No data available'** @@ -542,103 +548,115 @@ abstract class AppLocalizations { /// **'No games created yet'** String get no_games_created_yet; - /// No description provided for @no_groups_created_yet. + /// Message when no groups exist /// /// In en, this message translates to: /// **'No groups created yet'** String get no_groups_created_yet; - /// No description provided for @no_licenses_found. + /// Message when no licenses are found /// /// In en, this message translates to: /// **'No licenses found'** String get no_licenses_found; - /// No description provided for @no_license_text_available. + /// Message when no license text is available /// /// In en, this message translates to: /// **'No license text available'** String get no_license_text_available; - /// No description provided for @no_matches_created_yet. + /// Message when no matches exist /// /// In en, this message translates to: /// **'No matches created yet'** String get no_matches_created_yet; - /// No description provided for @no_players_created_yet. + /// Message when no players exist /// /// In en, this message translates to: /// **'No players created yet'** String get no_players_created_yet; - /// No description provided for @no_players_found_with_that_name. + /// Message when search returns no results /// /// In en, this message translates to: /// **'No players found with that name'** String get no_players_found_with_that_name; - /// No description provided for @no_players_selected. + /// Message when no players are selected /// /// In en, this message translates to: /// **'No players selected'** String get no_players_selected; - /// No description provided for @no_recent_matches_available. + /// Message when no recent matches exist /// /// In en, this message translates to: /// **'No recent matches available'** String get no_recent_matches_available; - /// No description provided for @no_results_entered_yet. + /// Message when no results have been entered yet /// /// In en, this message translates to: /// **'No results entered yet'** String get no_results_entered_yet; - /// No description provided for @no_second_match_available. + /// Message when no second match exists /// /// In en, this message translates to: /// **'No second match available'** String get no_second_match_available; - /// No description provided for @no_statistics_available. + /// Message when no statistics are available, because no matches were played yet /// /// In en, this message translates to: /// **'No statistics available'** String get no_statistics_available; - /// No description provided for @none. + /// None option label /// /// In en, this message translates to: /// **'None'** String get none; - /// No description provided for @none_group. + /// None group option label /// /// In en, this message translates to: /// **'None'** String get none_group; - /// No description provided for @not_available. + /// Abbreviation for not available /// /// In en, this message translates to: /// **'Not available'** String get not_available; - /// No description provided for @played_matches. + /// Title for placement ruleset + /// + /// In en, this message translates to: + /// **'Placement'** + String get placement; + + /// Label for placement text in match detail view + /// + /// In en, this message translates to: + /// **'place'** + String get place; + + /// Label for played matches statistic /// /// In en, this message translates to: /// **'Played Matches'** String get played_matches; - /// No description provided for @player_name. + /// Placeholder for player name input /// /// In en, this message translates to: /// **'Player name'** String get player_name; - /// No description provided for @players. + /// Players label /// /// In en, this message translates to: /// **'Players'** @@ -650,115 +668,121 @@ abstract class AppLocalizations { /// **'Point'** String get point; - /// No description provided for @points. + /// Points label /// /// In en, this message translates to: /// **'Points'** String get points; - /// No description provided for @privacy_policy. + /// Privacy policy menu item /// /// In en, this message translates to: /// **'Privacy Policy'** String get privacy_policy; - /// No description provided for @quick_create. + /// Title for quick create section /// /// In en, this message translates to: /// **'Quick Create'** String get quick_create; - /// No description provided for @recent_matches. + /// Title for recent matches section /// /// In en, this message translates to: /// **'Recent Matches'** String get recent_matches; - /// No description provided for @results. + /// Label for match results /// /// In en, this message translates to: /// **'Results'** String get results; - /// No description provided for @ruleset. + /// Ruleset label /// /// In en, this message translates to: /// **'Ruleset'** String get ruleset; - /// No description provided for @ruleset_least_points. + /// Description for least points ruleset /// /// In en, this message translates to: /// **'Inverse scoring: the player with the fewest points wins.'** String get ruleset_least_points; - /// No description provided for @ruleset_most_points. + /// Description for most points ruleset /// /// In en, this message translates to: /// **'Traditional ruleset: the player with the most points wins.'** String get ruleset_most_points; - /// No description provided for @ruleset_single_loser. + /// Description for placement ruleset + /// + /// In en, this message translates to: + /// **'Players can be arranged in an order, which reflects their placement.'** + String get ruleset_placement; + + /// Description for single loser ruleset /// /// In en, this message translates to: /// **'Exactly one loser is determined; last place receives the penalty or consequence.'** String get ruleset_single_loser; - /// No description provided for @ruleset_single_winner. + /// Description for single winner ruleset /// /// In en, this message translates to: /// **'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.'** String get ruleset_single_winner; - /// No description provided for @save_changes. + /// Save changes button text /// /// In en, this message translates to: /// **'Save Changes'** String get save_changes; - /// No description provided for @search_for_groups. + /// Hint text for group search input field /// /// In en, this message translates to: /// **'Search for groups'** String get search_for_groups; - /// No description provided for @search_for_players. + /// Hint text for player search input field /// /// In en, this message translates to: /// **'Search for players'** String get search_for_players; - /// No description provided for @select_winner. + /// Label to select the winner /// /// In en, this message translates to: /// **'Select Winner'** String get select_winner; - /// No description provided for @select_loser. + /// Label to select the loser /// /// In en, this message translates to: /// **'Select Loser'** String get select_loser; - /// No description provided for @selected_players. + /// Shows the number of selected players /// /// In en, this message translates to: /// **'Selected players'** String get selected_players; - /// No description provided for @settings. + /// Label for the App Settings /// /// In en, this message translates to: /// **'Settings'** String get settings; - /// No description provided for @single_loser. + /// Title for single loser ruleset /// /// In en, this message translates to: /// **'Single Loser'** String get single_loser; - /// No description provided for @single_winner. + /// Title for single winner ruleset /// /// In en, this message translates to: /// **'Single Winner'** @@ -788,13 +812,13 @@ abstract class AppLocalizations { /// **'Multiple Winners'** String get multiple_winners; - /// No description provided for @statistics. + /// Statistics tab label /// /// In en, this message translates to: /// **'Statistics'** String get statistics; - /// No description provided for @stats. + /// Stats tab label (short) /// /// In en, this message translates to: /// **'Stats'** @@ -812,13 +836,13 @@ abstract class AppLocalizations { /// **'There are no games matching your search'** String get there_are_no_games_matching_your_search; - /// No description provided for @there_is_no_group_matching_your_search. + /// Message when search returns no groups /// /// In en, this message translates to: /// **'There is no group matching your search'** String get there_is_no_group_matching_your_search; - /// No description provided for @this_cannot_be_undone. + /// Warning message for irreversible actions /// /// In en, this message translates to: /// **'This can\'t be undone.'** @@ -830,43 +854,43 @@ abstract class AppLocalizations { /// **'Tie'** String get tie; - /// No description provided for @today_at. + /// Date format for today /// /// In en, this message translates to: /// **'Today at'** String get today_at; - /// No description provided for @undo. + /// Undo button text /// /// In en, this message translates to: /// **'Undo'** String get undo; - /// No description provided for @unknown_exception. + /// Error message for unknown exceptions /// /// In en, this message translates to: /// **'Unknown Exception (see console)'** String get unknown_exception; - /// No description provided for @winner. + /// Winner label /// /// In en, this message translates to: /// **'Winner'** String get winner; - /// No description provided for @winrate. + /// Label for winrate statistic /// /// In en, this message translates to: /// **'Winrate'** String get winrate; - /// No description provided for @wins. + /// Label for wins statistic /// /// In en, this message translates to: /// **'Wins'** String get wins; - /// No description provided for @yesterday_at. + /// Date format for yesterday /// /// In en, this message translates to: /// **'Yesterday at'** diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 3666d11..f91e0ba 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -101,7 +101,7 @@ class AppLocalizationsDe extends AppLocalizations { String get data_successfully_imported => 'Daten erfolgreich importiert'; @override - String days_ago(Object count) { + String days_ago(int count) { return 'vor $count Tagen'; } @@ -131,6 +131,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get delete_match => 'Spiel löschen'; + @override + String get drag_to_set_placement => 'Ziehen um Platzierung zu setzen'; + @override String get description => 'Beschreibung'; @@ -289,6 +292,12 @@ class AppLocalizationsDe extends AppLocalizations { @override String get not_available => 'Nicht verfügbar'; + @override + String get placement => 'Platzierung'; + + @override + String get place => 'Platz'; + @override String get played_matches => 'Gespielte Spiele'; @@ -327,6 +336,10 @@ class AppLocalizationsDe extends AppLocalizations { String get ruleset_most_points => 'Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.'; + @override + String get ruleset_placement => + 'Spieler:innen können in einer Reihenfolge angeordnet werden, die ihre Platzierung reflektiert.'; + @override String get ruleset_single_loser => 'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index ae7d813..3bc7b2f 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -101,7 +101,7 @@ class AppLocalizationsEn extends AppLocalizations { String get data_successfully_imported => 'Data successfully imported'; @override - String days_ago(Object count) { + String days_ago(int count) { return '$count days ago'; } @@ -131,6 +131,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get delete_match => 'Delete Match'; + @override + String get drag_to_set_placement => 'Drag to set placement'; + @override String get description => 'Description'; @@ -289,6 +292,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get not_available => 'Not available'; + @override + String get placement => 'Placement'; + + @override + String get place => 'place'; + @override String get played_matches => 'Played Matches'; @@ -327,6 +336,10 @@ class AppLocalizationsEn extends AppLocalizations { String get ruleset_most_points => 'Traditional ruleset: the player with the most points wins.'; + @override + String get ruleset_placement => + 'Players can be arranged in an order, which reflects their placement.'; + @override String get ruleset_single_loser => 'Exactly one loser is determined; last place receives the penalty or consequence.'; From 0eb27ab2848f756e228833765782f8ac9c2dd7ed Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sat, 9 May 2026 19:48:02 +0200 Subject: [PATCH 17/20] add icon for placement ruleset --- lib/core/common.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/core/common.dart b/lib/core/common.dart index b298048..495759c 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -81,6 +81,8 @@ IconData getRulesetIcon(Ruleset ruleset) { return Icons.sentiment_dissatisfied; case Ruleset.multipleWinners: return Icons.group; + case Ruleset.placement: + return Icons.leaderboard; } } From 518bbb407c4e88bc02fb2116f2785721fb10538a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 9 May 2026 19:54:24 +0200 Subject: [PATCH 18/20] Updated icon and match tile icon style --- lib/core/common.dart | 3 ++- .../widgets/tiles/match_tile.dart | 20 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/core/common.dart b/lib/core/common.dart index 495759c..312e3fa 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:fluttericon/rpg_awesome_icons.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/player.dart'; @@ -82,7 +83,7 @@ IconData getRulesetIcon(Ruleset ruleset) { case Ruleset.multipleWinners: return Icons.group; case Ruleset.placement: - return Icons.leaderboard; + return RpgAwesome.podium; } } diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index 7c86c27..d034763 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -269,23 +269,21 @@ class _MatchTileState extends State { } Icon getMvpIcon() { - const Icon(Icons.emoji_events, size: 20, color: Colors.amber); + final icon = getRulesetIcon(widget.match.game.ruleset); switch (widget.match.game.ruleset) { case Ruleset.singleWinner: - return const Icon(Icons.emoji_events, size: 20, color: Colors.amber); + return Icon(icon, size: 20, color: Colors.amber); case Ruleset.singleLoser: - return const Icon( - Icons.sentiment_dissatisfied_outlined, - size: 20, - color: Colors.blue, - ); + return Icon(icon, size: 20, color: Colors.blue); case Ruleset.lowestScore: - return const Icon(Icons.arrow_downward, size: 20, color: Colors.orange); + return Icon(icon, size: 20, color: Colors.orange); case Ruleset.highestScore: - return const Icon(Icons.arrow_upward, size: 20, color: Colors.green); - default: - return const Icon(Icons.emoji_events, size: 20, color: Colors.amber); + return Icon(icon, size: 20, color: Colors.green); + case Ruleset.multipleWinners: + return Icon(icon, size: 20, color: Colors.amber); + case Ruleset.placement: + return Icon(icon, size: 20, color: Colors.deepOrangeAccent); } } } From f0ff4fbfc027ed2aa3ec4c083e0471b34c040a01 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 9 May 2026 20:32:39 +0200 Subject: [PATCH 19/20] feat: placement text color --- .../match_view/match_detail_view.dart | 59 +++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/lib/presentation/views/main_menu/match_view/match_detail_view.dart b/lib/presentation/views/main_menu/match_view/match_detail_view.dart index 9d1049e..04e7505 100644 --- a/lib/presentation/views/main_menu/match_view/match_detail_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_detail_view.dart @@ -272,7 +272,7 @@ class _MatchDetailViewState extends State { children: getSingleResultRow(loc), ); } else { - return getScoreResultWidget(loc); + return getMultiResultRows(loc); } } @@ -323,7 +323,7 @@ class _MatchDetailViewState extends State { } /// Returns the result widget for scores or placement - Widget getScoreResultWidget(AppLocalizations loc) { + Widget getMultiResultRows(AppLocalizations loc) { List<(String, int)> playerScores = []; for (var player in match.players) { int score = match.scores[player.id]?.score ?? 0; @@ -351,33 +351,58 @@ class _MatchDetailViewState extends State { color: CustomTheme.textColor, ), ), - Text( - ruleset == Ruleset.placement - ? getPlacementText(i + 1, context, loc) - : getPointLabel(loc, playerScores[i].$2), - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: CustomTheme.primaryColor, - ), - ), + getResultValueText(loc, i, playerScores[i].$2), ], ), ], ); } + Widget getResultValueText(AppLocalizations loc, int index, int score) { + final ruleset = match.game.ruleset; + + if (ruleset == Ruleset.placement) { + return Text( + getPlacementText(context, index + 1), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: getPlacementTextcolor(index), + ), + ); + } else { + return Text( + getPointLabel(loc, score), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: CustomTheme.primaryColor, + ), + ); + } + } + + Color getPlacementTextcolor(int placement) { + switch (placement) { + case 0: + return const Color(0xFFFFBF00); + case 1: + return const Color(0xBBFFFFFF); + case 2: + return const Color(0xFFCD7F32); + default: + return CustomTheme.textColor; + } + } + // Returns if the result can be displayed in a single row bool isSingleRowResult() { return match.game.ruleset == Ruleset.singleWinner || match.game.ruleset == Ruleset.singleLoser; } - String getPlacementText( - int rank, - BuildContext context, - AppLocalizations loc, - ) { + String getPlacementText(BuildContext context, int rank) { + final loc = AppLocalizations.of(context); final locale = Localizations.localeOf(context).languageCode; if (locale == 'de') { From df73fc87ebd648574216d9e99c6b92b169ba66df Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 9 May 2026 23:16:56 +0200 Subject: [PATCH 20/20] localizations --- lib/l10n/generated/app_localizations.dart | 218 +++++++++---------- lib/l10n/generated/app_localizations_de.dart | 2 +- lib/l10n/generated/app_localizations_en.dart | 2 +- 3 files changed, 111 insertions(+), 111 deletions(-) diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 3fa991e..bfdb659 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -98,37 +98,37 @@ abstract class AppLocalizations { Locale('en'), ]; - /// Label for all players list + /// No description provided for @all_players. /// /// In en, this message translates to: /// **'All players'** String get all_players; - /// Message when all players are added to selection + /// No description provided for @all_players_selected. /// /// In en, this message translates to: /// **'All players selected'** String get all_players_selected; - /// Label for amount of matches statistic + /// No description provided for @amount_of_matches. /// /// In en, this message translates to: /// **'Amount of Matches'** String get amount_of_matches; - /// The name of the App + /// No description provided for @app_name. /// /// In en, this message translates to: /// **'Tallee'** String get app_name; - /// Label for best player statistic + /// No description provided for @best_player. /// /// In en, this message translates to: /// **'Best Player'** String get best_player; - /// Cancel button text + /// No description provided for @cancel. /// /// In en, this message translates to: /// **'Cancel'** @@ -140,19 +140,19 @@ abstract class AppLocalizations { /// **'Choose Color'** String get choose_color; - /// Label for choosing a game + /// No description provided for @choose_game. /// /// In en, this message translates to: /// **'Choose Game'** String get choose_game; - /// Label for choosing a group + /// No description provided for @choose_group. /// /// In en, this message translates to: /// **'Choose Group'** String get choose_group; - /// Label for choosing a ruleset + /// No description provided for @choose_ruleset. /// /// In en, this message translates to: /// **'Choose Ruleset'** @@ -212,7 +212,7 @@ abstract class AppLocalizations { /// **'Yellow'** String get color_yellow; - /// Error message when adding a player fails + /// No description provided for @could_not_add_player. /// /// In en, this message translates to: /// **'Could not add player'** @@ -224,73 +224,73 @@ abstract class AppLocalizations { /// **'Create Game'** String get create_game; - /// Button text to create a group + /// No description provided for @create_group. /// /// In en, this message translates to: /// **'Create Group'** String get create_group; - /// Button text to create a match + /// No description provided for @create_match. /// /// In en, this message translates to: /// **'Create match'** String get create_match; - /// Appbar text to create a new group + /// No description provided for @create_new_group. /// /// In en, this message translates to: /// **'Create new group'** String get create_new_group; - /// Label for creation date + /// No description provided for @created_on. /// /// In en, this message translates to: /// **'Created on'** String get created_on; - /// Appbar text to create a new match + /// No description provided for @create_new_match. /// /// In en, this message translates to: /// **'Create new match'** String get create_new_match; - /// Data label + /// No description provided for @data. /// /// In en, this message translates to: /// **'Data'** String get data; - /// Success message after deleting data + /// No description provided for @data_successfully_deleted. /// /// In en, this message translates to: /// **'Data successfully deleted'** String get data_successfully_deleted; - /// Success message after exporting data + /// No description provided for @data_successfully_exported. /// /// In en, this message translates to: /// **'Data successfully exported'** String get data_successfully_exported; - /// Success message after importing data + /// No description provided for @data_successfully_imported. /// /// In en, this message translates to: /// **'Data successfully imported'** String get data_successfully_imported; - /// Date format for days ago + /// No description provided for @days_ago. /// /// In en, this message translates to: /// **'{count} days ago'** - String days_ago(int count); + String days_ago(Object count); - /// Delete button text + /// No description provided for @delete. /// /// In en, this message translates to: /// **'Delete'** String get delete; - /// Confirmation dialog for deleting all data + /// No description provided for @delete_all_data. /// /// In en, this message translates to: /// **'Delete all data'** @@ -308,19 +308,19 @@ abstract class AppLocalizations { /// **'If you delete this game template, {count, plural, =1{1 match} other{{count} matches}} using this game template will also be deleted.'** String delete_game_with_matches_warning(int count); - /// Confirmation dialog for deleting a group + /// No description provided for @delete_group. /// /// In en, this message translates to: /// **'Delete Group'** String get delete_group; - /// Button text to delete a match + /// No description provided for @delete_match. /// /// In en, this message translates to: /// **'Delete Match'** String get delete_match; - /// Label for dragging to set placement + /// No description provided for @drag_to_set_placement. /// /// In en, this message translates to: /// **'Drag to set placement'** @@ -338,31 +338,31 @@ abstract class AppLocalizations { /// **'Edit Game'** String get edit_game; - /// Button & Appbar label for editing a group + /// No description provided for @edit_group. /// /// In en, this message translates to: /// **'Edit Group'** String get edit_group; - /// Button & Appbar label for editing a match + /// No description provided for @edit_match. /// /// In en, this message translates to: /// **'Edit Match'** String get edit_match; - /// Label to enter players points + /// No description provided for @enter_points. /// /// In en, this message translates to: /// **'Enter points'** String get enter_points; - /// Button text to enter match results + /// No description provided for @enter_results. /// /// In en, this message translates to: /// **'Enter Results'** String get enter_results; - /// Error message when group creation fails + /// No description provided for @error_creating_group. /// /// In en, this message translates to: /// **'Error while creating group, please try again'** @@ -374,19 +374,19 @@ abstract class AppLocalizations { /// **'Error while deleting game, please try again'** String get error_deleting_game; - /// Error message when group deletion fails + /// No description provided for @error_deleting_group. /// /// In en, this message translates to: /// **'Error while deleting group, please try again'** String get error_deleting_group; - /// Error message when group editing fails + /// No description provided for @error_editing_group. /// /// 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 + /// No description provided for @error_reading_file. /// /// In en, this message translates to: /// **'Error reading file'** @@ -398,109 +398,109 @@ abstract class AppLocalizations { /// **'Exit View'** String get exit_view; - /// Message when export is canceled + /// No description provided for @export_canceled. /// /// In en, this message translates to: /// **'Export canceled'** String get export_canceled; - /// Export data menu item + /// No description provided for @export_data. /// /// In en, this message translates to: /// **'Export data'** String get export_data; - /// Error message for format exceptions + /// No description provided for @format_exception. /// /// In en, this message translates to: /// **'Format Exception (see console)'** String get format_exception; - /// Game label + /// No description provided for @game. /// /// In en, this message translates to: /// **'Game'** String get game; - /// Placeholder for game name search + /// No description provided for @game_name. /// /// In en, this message translates to: /// **'Game Name'** String get game_name; - /// Group label + /// No description provided for @group. /// /// In en, this message translates to: /// **'Group'** String get group; - /// Placeholder for group name input + /// No description provided for @group_name. /// /// In en, this message translates to: /// **'Group name'** String get group_name; - /// Title for group profile view + /// No description provided for @group_profile. /// /// In en, this message translates to: /// **'Group Profile'** String get group_profile; - /// Label for groups + /// No description provided for @groups. /// /// In en, this message translates to: /// **'Groups'** String get groups; - /// Home tab label + /// No description provided for @home. /// /// In en, this message translates to: /// **'Home'** String get home; - /// Message when import is canceled + /// No description provided for @import_canceled. /// /// In en, this message translates to: /// **'Import canceled'** String get import_canceled; - /// Import data menu item + /// No description provided for @import_data. /// /// In en, this message translates to: /// **'Import data'** String get import_data; - /// Info label + /// No description provided for @info. /// /// In en, this message translates to: /// **'Info'** String get info; - /// Error message for invalid schema + /// No description provided for @invalid_schema. /// /// In en, this message translates to: /// **'Invalid Schema'** String get invalid_schema; - /// Title for least points ruleset + /// No description provided for @least_points. /// /// In en, this message translates to: /// **'Least Points'** String get least_points; - /// Legal section header + /// No description provided for @legal. /// /// In en, this message translates to: /// **'Legal'** String get legal; - /// Legal notice menu item + /// No description provided for @legal_notice. /// /// In en, this message translates to: /// **'Legal Notice'** String get legal_notice; - /// Licenses menu item + /// No description provided for @licenses. /// /// In en, this message translates to: /// **'Licenses'** @@ -512,43 +512,43 @@ abstract class AppLocalizations { /// **'Live Edit Mode'** String get live_edit_mode; - /// Message when match is in progress + /// No description provided for @match_in_progress. /// /// In en, this message translates to: /// **'Match in progress...'** String get match_in_progress; - /// Placeholder for match name input + /// No description provided for @match_name. /// /// In en, this message translates to: /// **'Match name'** String get match_name; - /// Title for match profile view + /// No description provided for @match_profile. /// /// In en, this message translates to: /// **'Match Profile'** String get match_profile; - /// Label for matches + /// No description provided for @matches. /// /// In en, this message translates to: /// **'Matches'** String get matches; - /// Label for group members + /// No description provided for @members. /// /// In en, this message translates to: /// **'Members'** String get members; - /// Title for most points ruleset + /// No description provided for @most_points. /// /// In en, this message translates to: /// **'Most Points'** String get most_points; - /// Message when no data in the statistic tiles is given + /// No description provided for @no_data_available. /// /// In en, this message translates to: /// **'No data available'** @@ -560,115 +560,115 @@ abstract class AppLocalizations { /// **'No games created yet'** String get no_games_created_yet; - /// Message when no groups exist + /// No description provided for @no_groups_created_yet. /// /// In en, this message translates to: /// **'No groups created yet'** String get no_groups_created_yet; - /// Message when no licenses are found + /// No description provided for @no_licenses_found. /// /// In en, this message translates to: /// **'No licenses found'** String get no_licenses_found; - /// Message when no license text is available + /// No description provided for @no_license_text_available. /// /// In en, this message translates to: /// **'No license text available'** String get no_license_text_available; - /// Message when no matches exist + /// No description provided for @no_matches_created_yet. /// /// In en, this message translates to: /// **'No matches created yet'** String get no_matches_created_yet; - /// Message when no players exist + /// No description provided for @no_players_created_yet. /// /// In en, this message translates to: /// **'No players created yet'** String get no_players_created_yet; - /// Message when search returns no results + /// No description provided for @no_players_found_with_that_name. /// /// In en, this message translates to: /// **'No players found with that name'** String get no_players_found_with_that_name; - /// Message when no players are selected + /// No description provided for @no_players_selected. /// /// In en, this message translates to: /// **'No players selected'** String get no_players_selected; - /// Message when no recent matches exist + /// No description provided for @no_recent_matches_available. /// /// In en, this message translates to: /// **'No recent matches available'** String get no_recent_matches_available; - /// Message when no results have been entered yet + /// No description provided for @no_results_entered_yet. /// /// In en, this message translates to: /// **'No results entered yet'** String get no_results_entered_yet; - /// Message when no second match exists + /// No description provided for @no_second_match_available. /// /// In en, this message translates to: /// **'No second match available'** String get no_second_match_available; - /// Message when no statistics are available, because no matches were played yet + /// No description provided for @no_statistics_available. /// /// In en, this message translates to: /// **'No statistics available'** String get no_statistics_available; - /// None option label + /// No description provided for @none. /// /// In en, this message translates to: /// **'None'** String get none; - /// None group option label + /// No description provided for @none_group. /// /// In en, this message translates to: /// **'None'** String get none_group; - /// Abbreviation for not available + /// No description provided for @not_available. /// /// In en, this message translates to: /// **'Not available'** String get not_available; - /// Title for placement ruleset + /// No description provided for @placement. /// /// In en, this message translates to: /// **'Placement'** String get placement; - /// Label for placement text in match detail view + /// No description provided for @place. /// /// In en, this message translates to: /// **'place'** String get place; - /// Label for played matches statistic + /// No description provided for @played_matches. /// /// In en, this message translates to: /// **'Played Matches'** String get played_matches; - /// Placeholder for player name input + /// No description provided for @player_name. /// /// In en, this message translates to: /// **'Player name'** String get player_name; - /// Players label + /// No description provided for @players. /// /// In en, this message translates to: /// **'Players'** @@ -680,121 +680,121 @@ abstract class AppLocalizations { /// **'Point'** String get point; - /// Points label + /// No description provided for @points. /// /// In en, this message translates to: /// **'Points'** String get points; - /// Privacy policy menu item + /// No description provided for @privacy_policy. /// /// In en, this message translates to: /// **'Privacy Policy'** String get privacy_policy; - /// Title for quick create section + /// No description provided for @quick_create. /// /// In en, this message translates to: /// **'Quick Create'** String get quick_create; - /// Title for recent matches section + /// No description provided for @recent_matches. /// /// In en, this message translates to: /// **'Recent Matches'** String get recent_matches; - /// Label for match results + /// No description provided for @results. /// /// In en, this message translates to: /// **'Results'** String get results; - /// Ruleset label + /// No description provided for @ruleset. /// /// In en, this message translates to: /// **'Ruleset'** String get ruleset; - /// Description for least points ruleset + /// No description provided for @ruleset_least_points. /// /// In en, this message translates to: /// **'Inverse scoring: the player with the fewest points wins.'** String get ruleset_least_points; - /// Description for most points ruleset + /// No description provided for @ruleset_most_points. /// /// In en, this message translates to: /// **'Traditional ruleset: the player with the most points wins.'** String get ruleset_most_points; - /// Description for placement ruleset + /// No description provided for @ruleset_placement. /// /// In en, this message translates to: /// **'Players can be arranged in an order, which reflects their placement.'** String get ruleset_placement; - /// Description for single loser ruleset + /// No description provided for @ruleset_single_loser. /// /// In en, this message translates to: /// **'Exactly one loser is determined; last place receives the penalty or consequence.'** String get ruleset_single_loser; - /// Description for single winner ruleset + /// No description provided for @ruleset_single_winner. /// /// In en, this message translates to: /// **'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.'** String get ruleset_single_winner; - /// Save changes button text + /// No description provided for @save_changes. /// /// In en, this message translates to: /// **'Save Changes'** String get save_changes; - /// Hint text for group search input field + /// No description provided for @search_for_groups. /// /// In en, this message translates to: /// **'Search for groups'** String get search_for_groups; - /// Hint text for player search input field + /// No description provided for @search_for_players. /// /// In en, this message translates to: /// **'Search for players'** String get search_for_players; - /// Label to select the winner + /// No description provided for @select_winner. /// /// In en, this message translates to: /// **'Select Winner'** String get select_winner; - /// Label to select the loser + /// No description provided for @select_loser. /// /// In en, this message translates to: /// **'Select Loser'** String get select_loser; - /// Shows the number of selected players + /// No description provided for @selected_players. /// /// In en, this message translates to: /// **'Selected players'** String get selected_players; - /// Label for the App Settings + /// No description provided for @settings. /// /// In en, this message translates to: /// **'Settings'** String get settings; - /// Title for single loser ruleset + /// No description provided for @single_loser. /// /// In en, this message translates to: /// **'Single Loser'** String get single_loser; - /// Title for single winner ruleset + /// No description provided for @single_winner. /// /// In en, this message translates to: /// **'Single Winner'** @@ -824,13 +824,13 @@ abstract class AppLocalizations { /// **'Multiple Winners'** String get multiple_winners; - /// Statistics tab label + /// No description provided for @statistics. /// /// In en, this message translates to: /// **'Statistics'** String get statistics; - /// Stats tab label (short) + /// No description provided for @stats. /// /// In en, this message translates to: /// **'Stats'** @@ -848,13 +848,13 @@ abstract class AppLocalizations { /// **'There are no games matching your search'** String get there_are_no_games_matching_your_search; - /// Message when search returns no groups + /// No description provided for @there_is_no_group_matching_your_search. /// /// In en, this message translates to: /// **'There is no group matching your search'** String get there_is_no_group_matching_your_search; - /// Warning message for irreversible actions + /// No description provided for @this_cannot_be_undone. /// /// In en, this message translates to: /// **'This can\'t be undone.'** @@ -866,43 +866,43 @@ abstract class AppLocalizations { /// **'Tie'** String get tie; - /// Date format for today + /// No description provided for @today_at. /// /// In en, this message translates to: /// **'Today at'** String get today_at; - /// Undo button text + /// No description provided for @undo. /// /// In en, this message translates to: /// **'Undo'** String get undo; - /// Error message for unknown exceptions + /// No description provided for @unknown_exception. /// /// In en, this message translates to: /// **'Unknown Exception (see console)'** String get unknown_exception; - /// Winner label + /// No description provided for @winner. /// /// In en, this message translates to: /// **'Winner'** String get winner; - /// Label for winrate statistic + /// No description provided for @winrate. /// /// In en, this message translates to: /// **'Winrate'** String get winrate; - /// Label for wins statistic + /// No description provided for @wins. /// /// In en, this message translates to: /// **'Wins'** String get wins; - /// Date format for yesterday + /// No description provided for @yesterday_at. /// /// In en, this message translates to: /// **'Yesterday at'** diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 344eb38..8567ba0 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -101,7 +101,7 @@ class AppLocalizationsDe extends AppLocalizations { String get data_successfully_imported => 'Daten erfolgreich importiert'; @override - String days_ago(int count) { + String days_ago(Object count) { return 'vor $count Tagen'; } diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index a1ebd35..04e68b4 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -101,7 +101,7 @@ class AppLocalizationsEn extends AppLocalizations { String get data_successfully_imported => 'Data successfully imported'; @override - String days_ago(int count) { + String days_ago(Object count) { return '$count days ago'; }