Merge pull request 'Neuer Regelsatz: Platzierung' (#213) from feature/206-Neuer-Regelsatz-Platzierung into development
All checks were successful
Push Pipeline / test (push) Successful in 44s
Push Pipeline / update_version (push) Successful in 6s
Push Pipeline / generate_licenses (push) Successful in 36s
Push Pipeline / format (push) Successful in 55s
Push Pipeline / build (push) Successful in 5m25s

Reviewed-on: #213
Reviewed-by: Felix Kirchner <flixcoo@noreply.git.yannick-weigert.de>
This commit was merged in pull request #213.
This commit is contained in:
2026-05-09 21:37:30 +00:00
14 changed files with 309 additions and 38 deletions

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttericon/rpg_awesome_icons.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/player.dart';
@@ -18,6 +19,8 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) {
return loc.single_loser; return loc.single_loser;
case Ruleset.multipleWinners: case Ruleset.multipleWinners:
return loc.multiple_winners; return loc.multiple_winners;
case Ruleset.placement:
return loc.placement;
} }
} }
@@ -79,6 +82,8 @@ IconData getRulesetIcon(Ruleset ruleset) {
return Icons.sentiment_dissatisfied; return Icons.sentiment_dissatisfied;
case Ruleset.multipleWinners: case Ruleset.multipleWinners:
return Icons.group; return Icons.group;
case Ruleset.placement:
return RpgAwesome.podium;
} }
} }

View File

@@ -32,12 +32,14 @@ enum ExportResult { success, canceled, unknownException }
/// - [Ruleset.singleWinner]: The match is won by a single player. /// - [Ruleset.singleWinner]: The match is won by a single player.
/// - [Ruleset.singleLoser]: The match has a single loser. /// - [Ruleset.singleLoser]: The match has a single loser.
/// - [Ruleset.multipleWinners]: Multiple players can be winners. /// - [Ruleset.multipleWinners]: Multiple players can be winners.
/// - [Ruleset.placement]: The player with the highest placement wins.
enum Ruleset { enum Ruleset {
highestScore, highestScore,
lowestScore, lowestScore,
singleWinner, singleWinner,
singleLoser, singleLoser,
multipleWinners, multipleWinners,
placement,
} }
/// Different colors available for games /// Different colors available for games

View File

@@ -353,4 +353,19 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
return await deleteAllScoresForMatch(matchId: matchId); 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<void> setPlacements({
required String matchId,
required List<Player> 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),
);
}
}
} }

View File

@@ -156,6 +156,9 @@ class Match {
case Ruleset.multipleWinners: case Ruleset.multipleWinners:
return []; return [];
case Ruleset.placement:
return _getPlayersWithHighestScore().take(1).toList();
} }
} }

View File

@@ -44,6 +44,7 @@
}, },
"delete_group": "Gruppe löschen", "delete_group": "Gruppe löschen",
"delete_match": "Spiel löschen", "delete_match": "Spiel löschen",
"drag_to_set_placement": "Ziehen um Platzierung zu setzen",
"description": "Beschreibung", "description": "Beschreibung",
"edit_game": "Spielvorlage bearbeiten", "edit_game": "Spielvorlage bearbeiten",
"edit_group": "Gruppe bearbeiten", "edit_group": "Gruppe bearbeiten",
@@ -97,6 +98,8 @@
"none": "Kein", "none": "Kein",
"none_group": "Keine", "none_group": "Keine",
"not_available": "Nicht verfügbar", "not_available": "Nicht verfügbar",
"placement": "Platzierung",
"place": "Platz",
"played_matches": "Gespielte Spiele", "played_matches": "Gespielte Spiele",
"player_name": "Spieler:innenname", "player_name": "Spieler:innenname",
"players": "Spieler:innen", "players": "Spieler:innen",
@@ -110,6 +113,7 @@
"ruleset": "Regelwerk", "ruleset": "Regelwerk",
"ruleset_least_points": "Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.", "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_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_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.", "ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.",
"save_changes": "Änderungen speichern", "save_changes": "Änderungen speichern",

View File

@@ -45,6 +45,7 @@
}, },
"delete_group": "Delete Group", "delete_group": "Delete Group",
"delete_match": "Delete Match", "delete_match": "Delete Match",
"drag_to_set_placement": "Drag to set placement",
"description": "Description", "description": "Description",
"edit_game": "Edit Game", "edit_game": "Edit Game",
"edit_group": "Edit Group", "edit_group": "Edit Group",
@@ -98,6 +99,8 @@
"none": "None", "none": "None",
"none_group": "None", "none_group": "None",
"not_available": "Not available", "not_available": "Not available",
"placement": "Placement",
"place": "place",
"played_matches": "Played Matches", "played_matches": "Played Matches",
"player_name": "Player name", "player_name": "Player name",
"players": "Players", "players": "Players",
@@ -110,6 +113,7 @@
"ruleset": "Ruleset", "ruleset": "Ruleset",
"ruleset_least_points": "Inverse scoring: the player with the fewest points wins.", "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_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_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.", "ruleset_single_winner": "Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.",
"save_changes": "Save Changes", "save_changes": "Save Changes",

View File

@@ -320,6 +320,12 @@ abstract class AppLocalizations {
/// **'Delete Match'** /// **'Delete Match'**
String get delete_match; String get delete_match;
/// No description provided for @drag_to_set_placement.
///
/// In en, this message translates to:
/// **'Drag to set placement'**
String get drag_to_set_placement;
/// No description provided for @description. /// No description provided for @description.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -638,6 +644,18 @@ abstract class AppLocalizations {
/// **'Not available'** /// **'Not available'**
String get not_available; String get not_available;
/// No description provided for @placement.
///
/// In en, this message translates to:
/// **'Placement'**
String get placement;
/// No description provided for @place.
///
/// In en, this message translates to:
/// **'place'**
String get place;
/// No description provided for @played_matches. /// No description provided for @played_matches.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -710,6 +728,12 @@ abstract class AppLocalizations {
/// **'Traditional ruleset: the player with the most points wins.'** /// **'Traditional ruleset: the player with the most points wins.'**
String get ruleset_most_points; String get ruleset_most_points;
/// 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;
/// No description provided for @ruleset_single_loser. /// No description provided for @ruleset_single_loser.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -131,6 +131,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get delete_match => 'Spiel löschen'; String get delete_match => 'Spiel löschen';
@override
String get drag_to_set_placement => 'Ziehen um Platzierung zu setzen';
@override @override
String get description => 'Beschreibung'; String get description => 'Beschreibung';
@@ -295,6 +298,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get not_available => 'Nicht verfügbar'; String get not_available => 'Nicht verfügbar';
@override
String get placement => 'Platzierung';
@override
String get place => 'Platz';
@override @override
String get played_matches => 'Gespielte Spiele'; String get played_matches => 'Gespielte Spiele';
@@ -333,6 +342,10 @@ class AppLocalizationsDe extends AppLocalizations {
String get ruleset_most_points => String get ruleset_most_points =>
'Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.'; '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 @override
String get ruleset_single_loser => String get ruleset_single_loser =>
'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.'; 'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.';

View File

@@ -131,6 +131,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get delete_match => 'Delete Match'; String get delete_match => 'Delete Match';
@override
String get drag_to_set_placement => 'Drag to set placement';
@override @override
String get description => 'Description'; String get description => 'Description';
@@ -295,6 +298,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get not_available => 'Not available'; String get not_available => 'Not available';
@override
String get placement => 'Placement';
@override
String get place => 'place';
@override @override
String get played_matches => 'Played Matches'; String get played_matches => 'Played Matches';
@@ -333,6 +342,10 @@ class AppLocalizationsEn extends AppLocalizations {
String get ruleset_most_points => String get ruleset_most_points =>
'Traditional ruleset: the player with the most points wins.'; '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 @override
String get ruleset_single_loser => String get ruleset_single_loser =>
'Exactly one loser is determined; last place receives the penalty or consequence.'; 'Exactly one loser is determined; last place receives the penalty or consequence.';

View File

@@ -275,7 +275,7 @@ class _MatchDetailViewState extends State<MatchDetailView> {
children: getSingleResultRow(loc), children: getSingleResultRow(loc),
); );
} else { } else {
return getScoreResultWidget(loc); return getMultiResultRows(loc);
} }
} }
@@ -325,52 +325,113 @@ class _MatchDetailViewState extends State<MatchDetailView> {
} }
} }
/// Returns the result widget for scores /// Returns the result widget for scores or placement
Widget getScoreResultWidget(AppLocalizations loc) { Widget getMultiResultRows(AppLocalizations loc) {
List<(String, int)> playerScores = []; List<(String, int)> playerScores = [];
for (var player in match.players) { for (var player in match.players) {
int score = match.scores[player.id]?.score ?? 0; int score = match.scores[player.id]?.score ?? 0;
playerScores.add((player.name, score)); 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)); 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)); playerScores.sort((a, b) => a.$2.compareTo(b.$2));
} }
return Column( return Column(
children: [ children: [
for (var score in playerScores) for (var i = 0; i < playerScores.length; i++)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
score.$1, playerScores[i].$1,
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
color: CustomTheme.textColor, color: CustomTheme.textColor,
), ),
), ),
Text( getResultValueText(loc, i, playerScores[i].$2),
getPointLabel(loc, score.$2),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CustomTheme.primaryColor,
),
),
], ],
), ),
], ],
); );
} }
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 // Returns if the result can be displayed in a single row
bool isSingleRowResult() { bool isSingleRowResult() {
return match.game.ruleset == Ruleset.singleWinner || return match.game.ruleset == Ruleset.singleWinner ||
match.game.ruleset == Ruleset.singleLoser; match.game.ruleset == Ruleset.singleLoser;
} }
String getPlacementText(BuildContext context, int rank) {
final loc = AppLocalizations.of(context);
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';
}
}
void updateScoresForCurrentMatch() { void updateScoresForCurrentMatch() {
db.scoreEntryDao db.scoreEntryDao
.getAllMatchScores(matchId: match.id) .getAllMatchScores(matchId: match.id)

View File

@@ -11,6 +11,7 @@ import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart';
class MatchResultView extends StatefulWidget { class MatchResultView extends StatefulWidget {
/// A view that allows selecting and saving the winner of a match /// A view that allows selecting and saving the winner of a match
@@ -50,7 +51,7 @@ class _MatchResultViewState extends State<MatchResultView> {
@override @override
void initState() { void initState() {
db = Provider.of<AppDatabase>(context, listen: false); db = Provider.of<AppDatabase>(context, listen: false);
ruleset = Ruleset.highestScore; //widget.match.game.ruleset; ruleset = widget.match.game.ruleset;
canSave = !rulesetSupportsScoreEntry(); canSave = !rulesetSupportsScoreEntry();
allPlayers = widget.match.players; allPlayers = widget.match.players;
@@ -73,6 +74,12 @@ class _MatchResultViewState extends State<MatchResultView> {
final score = scoreList?.score ?? 0; final score = scoreList?.score ?? 0;
controller[i].text = score.toString(); 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(); super.initState();
} }
@@ -106,7 +113,7 @@ class _MatchResultViewState extends State<MatchResultView> {
child: Column( child: Column(
children: [ children: [
Expanded( Expanded(
child: isLiveEditMode && rulesetSupportsScoreEntry() child: isLiveEditMode
// Live Edit Mode // Live Edit Mode
? ListView.builder( ? ListView.builder(
itemCount: allPlayers.length, itemCount: allPlayers.length,
@@ -122,7 +129,7 @@ class _MatchResultViewState extends State<MatchResultView> {
); );
}, },
) )
// Normal Mode // Normal Container
: Container( : Container(
margin: const EdgeInsets.symmetric( margin: const EdgeInsets.symmetric(
horizontal: 12, horizontal: 12,
@@ -161,6 +168,7 @@ class _MatchResultViewState extends State<MatchResultView> {
}); });
}, },
child: ListView.builder( child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: allPlayers.length, itemCount: allPlayers.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return CustomRadioListTile( return CustomRadioListTile(
@@ -183,6 +191,7 @@ class _MatchResultViewState extends State<MatchResultView> {
), ),
), ),
), ),
// Show score entry // Show score entry
if (rulesetSupportsScoreEntry()) if (rulesetSupportsScoreEntry())
Expanded( Expanded(
@@ -205,6 +214,111 @@ class _MatchResultViewState extends State<MatchResultView> {
}, },
), ),
), ),
// Show draggable placement list
if (rulesetSupportsPlacement())
Expanded(
child: Row(
children: [
// Placement indicators
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Column(
children: [
for (
int i = 0;
i < allPlayers.length;
i++
)
Container(
alignment: Alignment.center,
height: 60,
child: Container(
decoration: BoxDecoration(
color:
CustomTheme.boxBorderColor,
borderRadius: CustomTheme
.standardBorderRadiusAll,
),
alignment: Alignment.center,
height: 50,
width: 50,
child: Text(
' #${i + 1} ',
style: const TextStyle(
color: CustomTheme.textColor,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
),
],
),
),
// Drag list
Expanded(
child: ReorderableListView.builder(
physics:
const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
proxyDecorator: (child, index, animation) {
return AnimatedBuilder(
animation: animation,
child: child,
builder: (context, child) {
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),
borderRadius: CustomTheme
.standardBorderRadiusAll,
),
),
),
],
);
},
);
},
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final Player item = allPlayers
.removeAt(oldIndex);
allPlayers.insert(newIndex, item);
});
},
itemCount: allPlayers.length,
itemBuilder: (context, index) {
return TextIconListTile(
key: ValueKey(allPlayers[index].id),
text: allPlayers[index].name,
icon: Icons.drag_handle,
);
},
),
),
],
),
),
], ],
), ),
), ),
@@ -266,6 +380,8 @@ class _MatchResultViewState extends State<MatchResultView> {
} else if (ruleset == Ruleset.lowestScore || } else if (ruleset == Ruleset.lowestScore ||
ruleset == Ruleset.highestScore) { ruleset == Ruleset.highestScore) {
await _handleScores(); await _handleScores();
} else if (ruleset == Ruleset.placement) {
await _handlePlacement();
} }
widget.onWinnerChanged?.call(); widget.onWinnerChanged?.call();
@@ -311,12 +427,22 @@ class _MatchResultViewState extends State<MatchResultView> {
} }
} }
/// Handles saving the placement for each player in the database.
Future<void> _handlePlacement() async {
await db.scoreEntryDao.setPlacements(
matchId: widget.match.id,
players: allPlayers,
);
}
String getTitleForRuleset(AppLocalizations loc) { String getTitleForRuleset(AppLocalizations loc) {
switch (ruleset) { switch (ruleset) {
case Ruleset.singleWinner: case Ruleset.singleWinner:
return loc.select_winner; return loc.select_winner;
case Ruleset.singleLoser: case Ruleset.singleLoser:
return loc.select_loser; return loc.select_loser;
case Ruleset.placement:
return loc.drag_to_set_placement;
default: default:
return loc.enter_points; return loc.enter_points;
} }
@@ -329,4 +455,8 @@ class _MatchResultViewState extends State<MatchResultView> {
bool rulesetSupportsScoreEntry() { bool rulesetSupportsScoreEntry() {
return ruleset == Ruleset.lowestScore || ruleset == Ruleset.highestScore; return ruleset == Ruleset.lowestScore || ruleset == Ruleset.highestScore;
} }
bool rulesetSupportsPlacement() {
return ruleset == Ruleset.placement;
}
} }

View File

@@ -196,6 +196,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
return TextIconListTile( return TextIconListTile(
text: suggestedPlayers[index].name, text: suggestedPlayers[index].name,
suffixText: getNameCountText(suggestedPlayers[index]), suffixText: getNameCountText(suggestedPlayers[index]),
icon: Icons.add,
onPressed: () { onPressed: () {
setState(() { setState(() {
// If the player is not already selected // If the player is not already selected

View File

@@ -261,30 +261,29 @@ class _MatchTileState extends State<MatchTile> {
final mvp = widget.match.mvp; final mvp = widget.match.mvp;
final mvpScore = widget.match.scores[mvp.first.id]?.score ?? 0; final mvpScore = widget.match.scores[mvp.first.id]?.score ?? 0;
final mvpNames = mvp.map((player) => player.name).join(', '); final mvpNames = mvp.map((player) => player.name).join(', ');
return '${loc.winner}: $mvpNames (${getPointLabel(loc, mvpScore)})'; 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.'; return '${loc.winner}: n.A.';
} }
Icon getMvpIcon() { Icon getMvpIcon() {
const Icon(Icons.emoji_events, size: 20, color: Colors.amber); final icon = getRulesetIcon(widget.match.game.ruleset);
switch (widget.match.game.ruleset) { switch (widget.match.game.ruleset) {
case Ruleset.singleWinner: 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: case Ruleset.singleLoser:
return const Icon( return Icon(icon, size: 20, color: Colors.blue);
Icons.sentiment_dissatisfied_outlined,
size: 20,
color: Colors.blue,
);
case Ruleset.lowestScore: 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: case Ruleset.highestScore:
return const Icon(Icons.arrow_upward, size: 20, color: Colors.green); return Icon(icon, size: 20, color: Colors.green);
default: case Ruleset.multipleWinners:
return const Icon(Icons.emoji_events, size: 20, color: Colors.amber); return Icon(icon, size: 20, color: Colors.amber);
case Ruleset.placement:
return Icon(icon, size: 20, color: Colors.deepOrangeAccent);
} }
} }
} }

View File

@@ -10,7 +10,7 @@ class TextIconListTile extends StatelessWidget {
super.key, super.key,
required this.text, required this.text,
this.suffixText = '', this.suffixText = '',
this.iconEnabled = true, this.icon,
this.onPressed, this.onPressed,
}); });
@@ -20,8 +20,8 @@ class TextIconListTile extends StatelessWidget {
/// An optional suffix text to display after the main text. /// An optional suffix text to display after the main text.
final String suffixText; final String suffixText;
/// A boolean to determine if the icon should be displayed. /// The icon to display in the tile.
final bool iconEnabled; final IconData? icon;
/// The callback to be invoked when the icon is pressed. /// The callback to be invoked when the icon is pressed.
final VoidCallback? onPressed; final VoidCallback? onPressed;
@@ -64,11 +64,8 @@ class TextIconListTile extends StatelessWidget {
), ),
), ),
), ),
if (iconEnabled) if (icon != null)
GestureDetector( GestureDetector(onTap: onPressed, child: Icon(icon, size: 20)),
onTap: onPressed,
child: const Icon(Icons.add, size: 20),
),
], ],
), ),
); );