Gruppenprofile implementieren #172

Merged
sneeex merged 11 commits from feature/169-gruppenprofile-implementieren into development 2026-01-17 20:34:27 +00:00
9 changed files with 479 additions and 53 deletions
Showing only changes of commit a8129eb134 - Show all commits

View File

@@ -4,6 +4,7 @@
"all_players_selected": "Alle Spieler:innen ausgewählt", "all_players_selected": "Alle Spieler:innen ausgewählt",
"amount_of_matches": "Anzahl der Spiele", "amount_of_matches": "Anzahl der Spiele",
"app_name": "Game Tracker", "app_name": "Game Tracker",
"best_player": "Beste:r Spieler:in",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"choose_game": "Spielvorlage wählen", "choose_game": "Spielvorlage wählen",
"choose_group": "Gruppe wählen", "choose_group": "Gruppe wählen",
@@ -13,6 +14,7 @@
"create_match": "Spiel erstellen", "create_match": "Spiel erstellen",
"create_new_group": "Neue Gruppe erstellen", "create_new_group": "Neue Gruppe erstellen",
"create_new_match": "Neues Spiel erstellen", "create_new_match": "Neues Spiel erstellen",
"created_on": "Erstellt am",
"data": "Daten", "data": "Daten",
"data_successfully_deleted": "Daten erfolgreich gelöscht", "data_successfully_deleted": "Daten erfolgreich gelöscht",
"data_successfully_exported": "Daten erfolgreich exportiert", "data_successfully_exported": "Daten erfolgreich exportiert",
@@ -20,6 +22,8 @@
"days_ago": "vor {count} Tagen", "days_ago": "vor {count} Tagen",
"delete": "Löschen", "delete": "Löschen",
"delete_all_data": "Alle Daten löschen", "delete_all_data": "Alle Daten löschen",
"delete_group": "Gruppe löschen",
"edit_group": "Gruppe bearbeiten",
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen", "error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
"error_reading_file": "Fehler beim Lesen der Datei", "error_reading_file": "Fehler beim Lesen der Datei",
"export_canceled": "Export abgebrochen", "export_canceled": "Export abgebrochen",
@@ -29,6 +33,7 @@
"game_name": "Spielvorlagenname", "game_name": "Spielvorlagenname",
"group": "Gruppe", "group": "Gruppe",
"group_name": "Gruppenname", "group_name": "Gruppenname",
"group_profile": "Gruppenprofil",
"groups": "Gruppen", "groups": "Gruppen",
"home": "Startseite", "home": "Startseite",
"import_canceled": "Import abgebrochen", "import_canceled": "Import abgebrochen",
@@ -42,6 +47,7 @@
"match_in_progress": "Spiel läuft...", "match_in_progress": "Spiel läuft...",
"match_name": "Spieltitel", "match_name": "Spieltitel",
"matches": "Spiele", "matches": "Spiele",
"members": "Mitglieder",
"most_points": "Höchste Punkte", "most_points": "Höchste Punkte",
"no_data_available": "Keine Daten verfügbar", "no_data_available": "Keine Daten verfügbar",
"no_groups_created_yet": "Noch keine Gruppen erstellt", "no_groups_created_yet": "Noch keine Gruppen erstellt",
@@ -57,6 +63,7 @@
"none": "Kein", "none": "Kein",
"none_group": "Keine", "none_group": "Keine",
"not_available": "Nicht verfügbar", "not_available": "Nicht verfügbar",
"played_matches": "Gespielte Spiele",
"player_name": "Spieler:innenname", "player_name": "Spieler:innenname",
"players": "Spieler:innen", "players": "Spieler:innen",
"players_count": "{count} Spieler", "players_count": "{count} Spieler",

View File

@@ -12,6 +12,9 @@
"@app_name": { "@app_name": {
"description": "The name of the App" "description": "The name of the App"
}, },
"@best_player": {
"description": "Label for best player statistic"
},
"@cancel": { "@cancel": {
"description": "Cancel button text" "description": "Cancel button text"
}, },
@@ -39,6 +42,9 @@
"@create_new_match": { "@create_new_match": {
"description": "Button text to create a new match" "description": "Button text to create a new match"
}, },
"@created_on": {
"description": "Label for creation date"
},
"@data": { "@data": {
"description": "Data label" "description": "Data label"
}, },
@@ -65,6 +71,12 @@
"@delete_all_data": { "@delete_all_data": {
"description": "Confirmation dialog for deleting all data" "description": "Confirmation dialog for deleting all data"
}, },
"@delete_group": {
"description": "Button text to delete a group"
},
"@edit_group": {
"description": "Button text to edit a group"
},
"@error_creating_group": { "@error_creating_group": {
"description": "Error message when group creation fails" "description": "Error message when group creation fails"
}, },
@@ -92,6 +104,9 @@
"@group_name": { "@group_name": {
"description": "Placeholder for group name input" "description": "Placeholder for group name input"
}, },
"@group_profile": {
"description": "Title for group profile view"
},
"@groups": { "@groups": {
"description": "Label for groups" "description": "Label for groups"
}, },
@@ -131,6 +146,9 @@
"@matches": { "@matches": {
"description": "Label for matches" "description": "Label for matches"
}, },
"@members": {
"description": "Label for group members"
},
"@most_points": { "@most_points": {
"description": "Title for most points ruleset" "description": "Title for most points ruleset"
}, },
@@ -176,6 +194,9 @@
"@not_available": { "@not_available": {
"description": "Abbreviation for not available" "description": "Abbreviation for not available"
}, },
"@played_matches": {
"description": "Label for played matches statistic"
},
"@player_name": { "@player_name": {
"description": "Placeholder for player name input" "description": "Placeholder for player name input"
}, },
@@ -281,6 +302,7 @@
"all_players_selected": "All players selected", "all_players_selected": "All players selected",
"amount_of_matches": "Amount of Matches", "amount_of_matches": "Amount of Matches",
"app_name": "Game Tracker", "app_name": "Game Tracker",
"best_player": "Best Player",
"cancel": "Cancel", "cancel": "Cancel",
"choose_game": "Choose Game", "choose_game": "Choose Game",
"choose_group": "Choose Group", "choose_group": "Choose Group",
@@ -289,6 +311,7 @@
"create_group": "Create Group", "create_group": "Create Group",
"create_match": "Create match", "create_match": "Create match",
"create_new_group": "Create new group", "create_new_group": "Create new group",
"created_on": "Created on",
"create_new_match": "Create new match", "create_new_match": "Create new match",
"data": "Data", "data": "Data",
"data_successfully_deleted": "Data successfully deleted", "data_successfully_deleted": "Data successfully deleted",
@@ -297,6 +320,8 @@
"days_ago": "{count} days ago", "days_ago": "{count} days ago",
"delete": "Delete", "delete": "Delete",
"delete_all_data": "Delete all data", "delete_all_data": "Delete all data",
"delete_group": "Delete Group",
"edit_group": "Edit Group",
"error_creating_group": "Error while creating group, please try again", "error_creating_group": "Error while creating group, please try again",
"error_reading_file": "Error reading file", "error_reading_file": "Error reading file",
"export_canceled": "Export canceled", "export_canceled": "Export canceled",
@@ -306,6 +331,7 @@
"game_name": "Game Name", "game_name": "Game Name",
"group": "Group", "group": "Group",
"group_name": "Group name", "group_name": "Group name",
"group_profile": "Group Profile",
"groups": "Groups", "groups": "Groups",
"home": "Home", "home": "Home",
"import_canceled": "Import canceled", "import_canceled": "Import canceled",
@@ -319,6 +345,7 @@
"match_in_progress": "Match in progress...", "match_in_progress": "Match in progress...",
"match_name": "Match name", "match_name": "Match name",
"matches": "Matches", "matches": "Matches",
"members": "Members",
"most_points": "Most Points", "most_points": "Most Points",
"no_data_available": "No data available", "no_data_available": "No data available",
"no_groups_created_yet": "No groups created yet", "no_groups_created_yet": "No groups created yet",
@@ -334,6 +361,7 @@
"none": "None", "none": "None",
"none_group": "None", "none_group": "None",
"not_available": "Not available", "not_available": "Not available",
"played_matches": "Played Matches",
"player_name": "Player name", "player_name": "Player name",
"players": "Players", "players": "Players",
"players_count": "{count} Players", "players_count": "{count} Players",

View File

@@ -122,6 +122,12 @@ abstract class AppLocalizations {
/// **'Game Tracker'** /// **'Game Tracker'**
String get app_name; String get app_name;
/// Label for best player statistic
///
/// In en, this message translates to:
/// **'Best Player'**
String get best_player;
/// Cancel button text /// Cancel button text
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -170,6 +176,12 @@ abstract class AppLocalizations {
/// **'Create new group'** /// **'Create new group'**
String get create_new_group; String get create_new_group;
/// Label for creation date
///
/// In en, this message translates to:
/// **'Created on'**
String get created_on;
/// Button text to create a new match /// Button text to create a new match
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -218,6 +230,18 @@ abstract class AppLocalizations {
/// **'Delete all data'** /// **'Delete all data'**
String get delete_all_data; String get delete_all_data;
/// Button text to delete a group
///
/// In en, this message translates to:
/// **'Delete Group'**
String get delete_group;
/// Button text to edit a group
///
/// In en, this message translates to:
/// **'Edit Group'**
String get edit_group;
/// Error message when group creation fails /// Error message when group creation fails
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -272,6 +296,12 @@ abstract class AppLocalizations {
/// **'Group name'** /// **'Group name'**
String get group_name; String get group_name;
/// Title for group profile view
///
/// In en, this message translates to:
/// **'Group Profile'**
String get group_profile;
/// Label for groups /// Label for groups
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -350,6 +380,12 @@ abstract class AppLocalizations {
/// **'Matches'** /// **'Matches'**
String get matches; String get matches;
/// Label for group members
///
/// In en, this message translates to:
/// **'Members'**
String get members;
/// Title for most points ruleset /// Title for most points ruleset
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -440,6 +476,12 @@ abstract class AppLocalizations {
/// **'Not available'** /// **'Not available'**
String get not_available; String get not_available;
/// Label for played matches statistic
///
/// In en, this message translates to:
/// **'Played Matches'**
String get played_matches;
/// Placeholder for player name input /// Placeholder for player name input
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -20,6 +20,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get app_name => 'Game Tracker'; String get app_name => 'Game Tracker';
@override
String get best_player => 'Beste:r Spieler:in';
@override @override
String get cancel => 'Abbrechen'; String get cancel => 'Abbrechen';
@@ -46,6 +49,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get create_new_group => 'Neue Gruppe erstellen'; String get create_new_group => 'Neue Gruppe erstellen';
@override
String get created_on => 'Erstellt am';
@override @override
String get create_new_match => 'Neues Spiel erstellen'; String get create_new_match => 'Neues Spiel erstellen';
@@ -72,6 +78,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get delete_all_data => 'Alle Daten löschen'; String get delete_all_data => 'Alle Daten löschen';
@override
String get delete_group => 'Gruppe löschen';
@override
String get edit_group => 'Gruppe bearbeiten';
@override @override
String get error_creating_group => String get error_creating_group =>
'Fehler beim Erstellen der Gruppe, bitte erneut versuchen'; 'Fehler beim Erstellen der Gruppe, bitte erneut versuchen';
@@ -100,6 +112,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get group_name => 'Gruppenname'; String get group_name => 'Gruppenname';
@override
String get group_profile => 'Gruppenprofil';
@override @override
String get groups => 'Gruppen'; String get groups => 'Gruppen';
@@ -139,6 +154,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get matches => 'Spiele'; String get matches => 'Spiele';
@override
String get members => 'Mitglieder';
@override @override
String get most_points => 'Höchste Punkte'; String get most_points => 'Höchste Punkte';
@@ -185,6 +203,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get not_available => 'Nicht verfügbar'; String get not_available => 'Nicht verfügbar';
@override
String get played_matches => 'Gespielte Spiele';
@override @override
String get player_name => 'Spieler:innenname'; String get player_name => 'Spieler:innenname';

View File

@@ -20,6 +20,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get app_name => 'Game Tracker'; String get app_name => 'Game Tracker';
@override
String get best_player => 'Best Player';
@override @override
String get cancel => 'Cancel'; String get cancel => 'Cancel';
@@ -46,6 +49,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get create_new_group => 'Create new group'; String get create_new_group => 'Create new group';
@override
String get created_on => 'Created on';
@override @override
String get create_new_match => 'Create new match'; String get create_new_match => 'Create new match';
@@ -72,6 +78,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get delete_all_data => 'Delete all data'; String get delete_all_data => 'Delete all data';
@override
String get delete_group => 'Delete Group';
@override
String get edit_group => 'Edit Group';
@override @override
String get error_creating_group => String get error_creating_group =>
'Error while creating group, please try again'; 'Error while creating group, please try again';
@@ -100,6 +112,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get group_name => 'Group name'; String get group_name => 'Group name';
@override
String get group_profile => 'Group Profile';
@override @override
String get groups => 'Groups'; String get groups => 'Groups';
@@ -139,6 +154,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get matches => 'Matches'; String get matches => 'Matches';
@override
String get members => 'Members';
@override @override
String get most_points => 'Most Points'; String get most_points => 'Most Points';
@@ -185,6 +203,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get not_available => 'Not available'; String get not_available => 'Not available';
@override
String get played_matches => 'Played Matches';
@override @override
String get player_name => 'Player name'; String get player_name => 'Player name';

View File

@@ -0,0 +1,271 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/adaptive_page_route.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart';
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
import 'package:game_tracker/presentation/widgets/buttons/animated_dialog_button.dart';
import 'package:game_tracker/presentation/widgets/buttons/main_menu_button.dart';
import 'package:game_tracker/presentation/widgets/custom_alert_dialog.dart';
import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart';
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
class GroupProfileView extends StatefulWidget {
/// A view that displays the profile of a group
/// - [group]: The group to display
const GroupProfileView({
super.key,
required this.group,
required this.callback,
});
/// The group to display
final Group group;
final VoidCallback callback;
@override
State<GroupProfileView> createState() => _GroupProfileViewState();
}
class _GroupProfileViewState extends State<GroupProfileView> {
late final AppDatabase db;
bool isLoading = true;
/// Total matches played in this group
int totalMatches = 0;
String bestPlayer = '';
@override
void initState() {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
_loadStatistics();
}
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar(
title: Text(loc.group_profile),
actions: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
showDialog<bool>(
context: context,
builder: (context) => CustomAlertDialog(
title: '${loc.delete_group}?',
content: loc.this_cannot_be_undone,
actions: [
AnimatedDialogButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(
loc.cancel,
style: const TextStyle(color: CustomTheme.textColor),
),
),
AnimatedDialogButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(
loc.delete,
style: TextStyle(color: CustomTheme.secondaryColor),
),
),
],
),
).then((confirmed) async {
if (confirmed! && context.mounted) {
await db.groupDao.deleteGroup(groupId: widget.group.id);
if (!context.mounted) return;
Navigator.pop(context);
widget.callback.call();
}
});
},
),
],
),
body: SafeArea(
child: Stack(
alignment: Alignment.center,
children: [
ListView(
padding: const EdgeInsets.only(
left: 12,
right: 12,
top: 20,
bottom: 100,
),
children: [
Center(
child: Container(
width: 55,
height: 55,
decoration: BoxDecoration(
color: CustomTheme.primaryColor.withGreen(40),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
Icons.group,
size: 38,
color: CustomTheme.secondaryColor,
),
),
),
const SizedBox(height: 10),
Text(
widget.group.name,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: CustomTheme.textColor,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 5),
Text(
"${loc.created_on} ${DateFormat('dd.MM.yyyy').format(widget.group.createdAt)}",
style: const TextStyle(
fontSize: 12,
color: CustomTheme.textColor,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
InfoTile(
title: loc.members,
icon: Icons.people,
content: Wrap(
spacing: 8,
runSpacing: 8,
children: widget.group.members.map((member) {
return TextIconTile(
text: member.name,
iconEnabled: false,
);
}).toList(),
),
),
const SizedBox(height: 15),
InfoTile(
title: loc.statistics,
icon: Icons.bar_chart,
content: AppSkeleton(
enabled: isLoading,
child: Column(
children: [
_buildStatRow(
loc.members,
widget.group.members.length.toString(),
),
_buildStatRow(
loc.played_matches,
totalMatches.toString(),
),
_buildStatRow(loc.best_player, bestPlayer),
],
),
),
),
],
),
Positioned(
bottom: MediaQuery.paddingOf(context).bottom,
child: MainMenuButton(
text: loc.edit_group,
icon: Icons.edit,
onPressed: () async {
await Navigator.push(
context,
adaptivePageRoute(
builder: (context) {
return const CreateGroupView();
},
),
);
},
),
),
],
),
),
);
}
/// Builds a single statistic row with a label and value
Widget _buildStatRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
label,
style: const TextStyle(
fontSize: 16,
color: CustomTheme.textColor,
),
),
],
),
Text(
value,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
);
}
/// Loads statistics for this group
Future<void> _loadStatistics() async {
final matches = await db.matchDao.getAllMatches();
final groupMatches = matches
.where((match) => match.group?.id == widget.group.id)
.toList();
setState(() {
totalMatches = groupMatches.length;
bestPlayer = _getBestPlayer(groupMatches);
isLoading = false;
});
}
/// Determines the best player in the group based on match wins
String _getBestPlayer(List<Match> matches) {
final bestPlayerCounts = <Player, int>{};
// Count wins for each player
for (var match in matches) {
if (match.winner != null) {
bestPlayerCounts.update(
match.winner!,
(value) => value + 1,
ifAbsent: () => 1,
);
}
}
// Sort players by win count
final sortedPlayers = bestPlayerCounts.entries.toList()
..sort((a, b) => b.value.compareTo(a.value));
// Get the best player
bestPlayer = sortedPlayers.isNotEmpty ? sortedPlayers.first.key.name : '-';
return bestPlayer;
}
}

View File

@@ -7,6 +7,7 @@ import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart'; import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart';
import 'package:game_tracker/presentation/views/main_menu/group_view/group_profile_view.dart';
import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
import 'package:game_tracker/presentation/widgets/buttons/main_menu_button.dart'; import 'package:game_tracker/presentation/widgets/buttons/main_menu_button.dart';
import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart';
@@ -74,7 +75,22 @@ class _GroupsViewState extends State<GroupsView> {
height: MediaQuery.paddingOf(context).bottom - 20, height: MediaQuery.paddingOf(context).bottom - 20,
); );
} }
return GroupTile(group: groups[index]); return GroupTile(
group: groups[index],
onTap: () async {
await Navigator.push(
context,
adaptivePageRoute(
builder: (context) {
return GroupProfileView(
group: groups[index],
callback: loadGroups,
);
},
),
);
},
);
}, },
), ),
), ),
@@ -105,6 +121,9 @@ class _GroupsViewState extends State<GroupsView> {
} }
void loadGroups() { void loadGroups() {
setState(() {
isLoading = true;
});
Future.wait([ Future.wait([
db.groupDao.getAllGroups(), db.groupDao.getAllGroups(),
Future.delayed(Constants.minimumSkeletonDuration), Future.delayed(Constants.minimumSkeletonDuration),

View File

@@ -3,11 +3,17 @@ import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
class GroupTile extends StatelessWidget { class GroupTile extends StatefulWidget {
/// A tile widget that displays information about a group, including its name and members. /// A tile widget that displays information about a group, including its name and members.
/// - [group]: The group data to be displayed. /// - [group]: The group data to be displayed.
/// - [isHighlighted]: Whether the tile should be highlighted. /// - [isHighlighted]: Whether the tile should be highlighted.
const GroupTile({super.key, required this.group, this.isHighlighted = false}); /// - [onTap]: Callback function to be executed when the tile is tapped.
const GroupTile({
super.key,
required this.group,
this.isHighlighted = false,
this.onTap,
});
/// The group data to be displayed. /// The group data to be displayed.
final Group group; final Group group;
@@ -15,61 +21,72 @@ class GroupTile extends StatelessWidget {
/// Whether the tile should be highlighted. /// Whether the tile should be highlighted.
final bool isHighlighted; final bool isHighlighted;
/// Callback function to be executed when the tile is tapped.
final VoidCallback? onTap;
@override
State<GroupTile> createState() => _GroupTileState();
}
class _GroupTileState extends State<GroupTile> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedContainer( return GestureDetector(
margin: CustomTheme.standardMargin, onTap: widget.onTap,
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), child: AnimatedContainer(
decoration: isHighlighted margin: CustomTheme.standardMargin,
? CustomTheme.highlightedBoxDecoration padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
: CustomTheme.standardBoxDecoration, decoration: widget.isHighlighted
duration: const Duration(milliseconds: 150), ? CustomTheme.highlightedBoxDecoration
child: Column( : CustomTheme.standardBoxDecoration,
crossAxisAlignment: CrossAxisAlignment.start, duration: const Duration(milliseconds: 150),
children: [ child: Column(
Row( crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ Row(
Flexible( mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: Text( children: [
group.name, Flexible(
overflow: TextOverflow.ellipsis, child: Text(
style: const TextStyle( widget.group.name,
fontWeight: FontWeight.bold, overflow: TextOverflow.ellipsis,
fontSize: 18,
),
),
),
Row(
children: [
Text(
'${group.members.length}',
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.w900, fontWeight: FontWeight.bold,
fontSize: 18, fontSize: 18,
), ),
), ),
const SizedBox(width: 3), ),
const Icon(Icons.group, size: 22), Row(
], children: [
), Text(
], '${widget.group.members.length}',
), style: const TextStyle(
const SizedBox(height: 5), fontWeight: FontWeight.w900,
Wrap( fontSize: 18,
alignment: WrapAlignment.start, ),
crossAxisAlignment: WrapCrossAlignment.start, ),
spacing: 12.0, const SizedBox(width: 3),
runSpacing: 8.0, const Icon(Icons.group, size: 22),
children: <Widget>[ ],
for (var member in [ ),
...group.members, ],
]..sort((a, b) => a.name.compareTo(b.name))) ),
TextIconTile(text: member.name, iconEnabled: false), const SizedBox(height: 5),
], Wrap(
), alignment: WrapAlignment.start,
const SizedBox(height: 2.5), crossAxisAlignment: WrapCrossAlignment.start,
], spacing: 12.0,
runSpacing: 8.0,
children: <Widget>[
for (var member in [
...widget.group.members,
]..sort((a, b) => a.name.compareTo(b.name)))
TextIconTile(text: member.name, iconEnabled: false),
],
),
const SizedBox(height: 2.5),
],
),
), ),
); );
} }

View File

@@ -1,7 +1,7 @@
name: game_tracker name: game_tracker
description: "Game Tracking App for Card Games" description: "Game Tracking App for Card Games"
publish_to: 'none' publish_to: 'none'
version: 0.0.7+212 version: 0.0.7+221
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1