diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ef497ae7..020a8a6e 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -163,6 +163,7 @@ "settings": "Settings", "signInAndUp": "Sign In & Sing Up", "signOut": "Sing out", + "share": "Share", "startedOn": "Started on {date} at {hour}", "@startedOn": { "placeholders": { diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 8be9c65b..303237cd 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -161,6 +161,7 @@ "score": "Score", "search": "Rechercher", "settings": "Paramètres", + "share": "Partager", "signInAndUp": "Connexion & Inscription", "signOut": "Déconnexion", "startedOn": "Commencée le {date} à {hour}", diff --git a/lib/models/player.dart b/lib/models/player.dart index 053d2034..29e408b3 100644 --- a/lib/models/player.dart +++ b/lib/models/player.dart @@ -19,6 +19,7 @@ class Player extends CargPlayerObject with ChangeNotifier { late String _profilePicture; late bool _selected; late bool _useGravatarProfilePicture; + late List _sharedWith; static const String defaultProfilePicture = 'https://firebasestorage.googleapis.com/v0/b/carg-d3732.appspot.com/o/carg_logo.png?alt=media&token=861511da-db26-4216-8ee6-29b20c0a6852'; @@ -34,6 +35,27 @@ class Player extends CargPlayerObject with ChangeNotifier { _gravatarProfilePicture = 'https://gravatar.com/avatar/$emailHash?s=200'; } + void sharePlayer(Player? player) { + if (player != null && player.linkedUserId != null) { + if (!sharedWith.contains(player.linkedUserId)) { + player.selected = true; + sharedWith.add(player.linkedUserId!); + notifyListeners(); + } else if (sharedWith.contains(player.linkedUserId)) { + player.selected = false; + sharedWith.remove(player.linkedUserId!); + notifyListeners(); + } + } + } + + List get sharedWith => _sharedWith; + + set sharedWith(List value) { + _sharedWith = value; + notifyListeners(); + } + bool get useGravatarProfilePicture => _useGravatarProfilePicture; set useGravatarProfilePicture(bool value) { @@ -74,6 +96,7 @@ class Player extends CargPlayerObject with ChangeNotifier { this.firstName, this.lastName, this.ownedBy, + sharedWith, userName, profilePicture, this.linkedUserId, @@ -89,6 +112,7 @@ class Player extends CargPlayerObject with ChangeNotifier { _profilePicture = profilePicture ?? defaultProfilePicture; _userName = userName ?? ''; _selected = false; + _sharedWith = sharedWith ?? []; _useGravatarProfilePicture = useGravatarProfilePicture ?? false; _gravatarProfilePicture = gravatarProfilePicture; } @@ -113,6 +137,7 @@ class Player extends CargPlayerObject with ChangeNotifier { linkedUserId: json?['linked_user_id'], profilePicture: json?['profile_picture'], ownedBy: json?['owned_by'], + sharedWith: json?['shared_with'], useGravatarProfilePicture: json?['use_gravatar_profile_picture'] ?? false, gravatarProfilePicture: json?['gravatar_profile_picture'], @@ -133,6 +158,7 @@ class Player extends CargPlayerObject with ChangeNotifier { 'use_gravatar_profile_picture': useGravatarProfilePicture, 'linked_user_id': linkedUserId, 'owned_by': ownedBy, + 'shared_with': sharedWith, 'owned': owned, 'testing': testing, 'admin': admin diff --git a/lib/services/impl/player_service.dart b/lib/services/impl/player_service.dart index 00491d99..ce8561b2 100644 --- a/lib/services/impl/player_service.dart +++ b/lib/services/impl/player_service.dart @@ -91,4 +91,32 @@ class PlayerService extends AbstractPlayerService { throw ServiceException('Error during the index search : ${e.toString()}'); } } + + @override + Future sharePlayer({Player? player, List? users}) async { + if (player == null || users == null) { + throw ServiceException('Please provide a player and an user list'); + } + try { + player.sharedWith?.addAll(users); + await playerRepository.update(player); + } on RepositoryException catch (e) { + throw throw ServiceException( + 'Impossible to modify the player ${player.id} : ${e.message}'); + } + } + + @override + Future unSharePlayer({Player? player}) async { + if (player == null) { + throw ServiceException('Please provide a player and an user list'); + } + try { + player.sharedWith?.clear(); + await playerRepository.update(player); + } on RepositoryException catch (e) { + throw throw ServiceException( + 'Impossible to modify the player ${player.id} : ${e.message}'); + } + } } diff --git a/lib/services/player/abstract_player_service.dart b/lib/services/player/abstract_player_service.dart index a436aa51..a70727d2 100644 --- a/lib/services/player/abstract_player_service.dart +++ b/lib/services/player/abstract_player_service.dart @@ -25,4 +25,12 @@ abstract class AbstractPlayerService extends BaseAbstractService { /// Return the player or null if not found Future> searchPlayers( {String query = '', Player? currentPlayer, bool? myPlayers}); + + /// Share a player with other users + Future sharePlayer( + {Player? player, List? users}); + + /// Remove all users with you shared the player + Future unSharePlayer( + {Player? player}); } diff --git a/lib/views/dialogs/player_info_dialog.dart b/lib/views/dialogs/player_info_dialog.dart index c36cfef4..61e5c586 100644 --- a/lib/views/dialogs/player_info_dialog.dart +++ b/lib/views/dialogs/player_info_dialog.dart @@ -1,3 +1,4 @@ +import 'package:carg/helpers/custom_route.dart'; import 'package:carg/models/game/game_type.dart'; import 'package:carg/models/player.dart'; import 'package:carg/services/auth/auth_service.dart'; @@ -5,11 +6,12 @@ import 'package:carg/services/player/abstract_player_service.dart'; import 'package:carg/styles/properties.dart'; import 'package:carg/styles/text_style.dart'; import 'package:carg/views/helpers/info_snackbar.dart'; +import 'package:carg/views/screens/share_user_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class PlayerInfoDialog extends StatelessWidget { final Player player; @@ -56,7 +58,8 @@ class PlayerInfoDialog extends StatelessWidget { titlePadding: const EdgeInsets.all(0), contentPadding: const EdgeInsets.symmetric(horizontal: 20), actionsPadding: const EdgeInsets.fromLTRB(0, 0, 20, 10), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(CustomProperties.borderRadius)), title: Container( decoration: BoxDecoration( color: player.getSideColor(context), @@ -80,14 +83,18 @@ class PlayerInfoDialog extends StatelessWidget { child: ElevatedButton.icon( key: const ValueKey('copyIDButton'), style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all(Colors.white), - foregroundColor: MaterialStateProperty.all( - player.getSideColor(context)), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - CustomProperties.borderRadius)))), + backgroundColor: + MaterialStateProperty.all(Colors.white), + foregroundColor: MaterialStateProperty.all( + player.getSideColor(context)), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + CustomProperties.borderRadius, + ), + ), + ), + ), onPressed: () async => {await _copyId(context)}, icon: const Icon(Icons.copy), label: Text(AppLocalizations.of(context)!.copyId), @@ -105,15 +112,20 @@ class PlayerInfoDialog extends StatelessWidget { builder: (context, playerData, _) => Padding( padding: const EdgeInsets.fromLTRB(0, 15, 15, 15), child: Container( - width: 60, - height: 60, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - width: 2, color: player.getSideColor(context)), - image: DecorationImage( - fit: BoxFit.fill, - image: NetworkImage(playerData.profilePicture)))), + width: 60, + height: 60, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + width: 2, color: player.getSideColor(context)), + image: DecorationImage( + fit: BoxFit.fill, + image: NetworkImage( + playerData.profilePicture, + ), + ), + ), + ), ), ), Flexible( @@ -122,33 +134,32 @@ class PlayerInfoDialog extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 10), child: Consumer( builder: (context, playerData, _) => TextFormField( - key: const ValueKey('usernameTextField'), - initialValue: playerData.userName, - enabled: playerData.owned, - style: const TextStyle( - fontSize: 25, fontWeight: FontWeight.bold), - maxLines: null, - onChanged: (value) => playerData.userName = value, - decoration: InputDecoration( - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: player.getSideColor(context), - width: 2), - ), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: player.getSideColor(context), - width: 2), - ), - disabledBorder: InputBorder.none, - labelStyle: - TextStyle(color: player.getSideColor(context)), - hintStyle: TextStyle( - fontSize: 25, - color: Theme.of(context).hintColor), - labelText: playerData.owned && isNewPlayer - ? AppLocalizations.of(context)!.username - : null)), + key: const ValueKey('usernameTextField'), + initialValue: playerData.userName, + enabled: playerData.owned, + style: const TextStyle( + fontSize: 25, fontWeight: FontWeight.bold), + maxLines: null, + onChanged: (value) => playerData.userName = value, + decoration: InputDecoration( + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: player.getSideColor(context), width: 2), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: player.getSideColor(context), width: 2), + ), + disabledBorder: InputBorder.none, + labelStyle: + TextStyle(color: player.getSideColor(context)), + hintStyle: TextStyle( + fontSize: 25, color: Theme.of(context).hintColor), + labelText: playerData.owned && isNewPlayer + ? AppLocalizations.of(context)!.username + : null, + ), + ), ), ), ), @@ -157,26 +168,27 @@ class PlayerInfoDialog extends StatelessWidget { if (isNewPlayer) Consumer( builder: (context, playerData, _) => TextFormField( - key: const ValueKey('profilePictureTextField'), - initialValue: playerData.profilePicture, - enabled: playerData.owned, - onChanged: (value) => playerData.profilePicture = value, - style: const TextStyle(fontSize: 20), - maxLines: null, - decoration: InputDecoration( - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: player.getSideColor(context), width: 2), - ), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: player.getSideColor(context), width: 2), - ), - labelStyle: - TextStyle(color: player.getSideColor(context)), - hintStyle: TextStyle( - fontSize: 15, color: Theme.of(context).hintColor), - labelText: AppLocalizations.of(context)!.profilePicture)), + key: const ValueKey('profilePictureTextField'), + initialValue: playerData.profilePicture, + enabled: playerData.owned, + onChanged: (value) => playerData.profilePicture = value, + style: const TextStyle(fontSize: 20), + maxLines: null, + decoration: InputDecoration( + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: player.getSideColor(context), width: 2), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: player.getSideColor(context), width: 2), + ), + labelStyle: TextStyle(color: player.getSideColor(context)), + hintStyle: TextStyle( + fontSize: 15, color: Theme.of(context).hintColor), + labelText: AppLocalizations.of(context)!.profilePicture, + ), + ), ), if (player.gameStatsList!.isNotEmpty) Padding( @@ -194,10 +206,15 @@ class PlayerInfoDialog extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ SizedBox( - width: 100, - child: Text('${stat.gameType.name} : ', - overflow: TextOverflow.ellipsis, - style: const TextStyle(fontSize: 22))), + width: 100, + child: Text( + '${stat.gameType.name} : ', + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 22, + ), + ), + ), const Padding( padding: EdgeInsets.symmetric(horizontal: 5.0), child: Icon(FontAwesomeIcons.trophy, size: 15), @@ -231,38 +248,79 @@ class PlayerInfoDialog extends StatelessWidget { key: const ValueKey('noStatsText'), textAlign: TextAlign.center) ]), ), - actions: [ - if (player.owned) - ElevatedButton.icon( - key: const ValueKey('saveButton'), - style: ButtonStyle( + actions: player.owned + ? [ + ElevatedButton( + key: const ValueKey('shareButton'), + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Theme.of(context).colorScheme.secondary), + foregroundColor: MaterialStateProperty.all( + Theme.of(context).cardColor), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + CustomProperties.borderRadius, + ), + ), + ), + ), + onPressed: () async => await Navigator.push( + context, + CustomRouteLeftToRight( + builder: (context) => ShareUserScreen( + playerService: playerService, + player: player, + ), + ), + ), + child: const Icon( + Icons.share, + ), + ), + ElevatedButton.icon( + key: const ValueKey('saveButton'), + style: ButtonStyle( backgroundColor: MaterialStateProperty.all( player.getSideColor(context)), foregroundColor: MaterialStateProperty.all( Theme.of(context).cardColor), shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - CustomProperties.borderRadius)))), - onPressed: () async => await _savePlayer(context), - label: Text(MaterialLocalizations.of(context).saveButtonLabel), - icon: const Icon(Icons.check)) - else - ElevatedButton.icon( - key: const ValueKey('closeButton'), - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.white), - foregroundColor: MaterialStateProperty.all( - player.getSideColor(context)), - shape: MaterialStateProperty.all( RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - CustomProperties.borderRadius)))), - onPressed: () => Navigator.pop(context), - icon: const Icon(Icons.close), - label: Text(MaterialLocalizations.of(context).closeButtonLabel), - ) - ], + borderRadius: BorderRadius.circular( + CustomProperties.borderRadius, + ), + ), + ), + ), + onPressed: () async => await _savePlayer(context), + label: Text(MaterialLocalizations.of(context).saveButtonLabel), + icon: const Icon( + Icons.check, + ), + ), + ] + : [ + ElevatedButton.icon( + key: const ValueKey('closeButton'), + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(Colors.white), + foregroundColor: MaterialStateProperty.all( + player.getSideColor(context)), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + CustomProperties.borderRadius, + ), + ), + ), + ), + onPressed: () => Navigator.pop(context), + icon: const Icon(Icons.close), + label: Text(MaterialLocalizations.of(context).closeButtonLabel), + ) + ], scrollable: true, ); } diff --git a/lib/views/screens/share_user_screen.dart b/lib/views/screens/share_user_screen.dart new file mode 100644 index 00000000..e6ddab6c --- /dev/null +++ b/lib/views/screens/share_user_screen.dart @@ -0,0 +1,144 @@ +import 'package:carg/models/player.dart'; +import 'package:carg/services/auth/auth_service.dart'; +import 'package:carg/services/impl/player_service.dart'; +import 'package:carg/services/player/abstract_player_service.dart'; +import 'package:carg/styles/properties.dart'; +import 'package:carg/styles/text_style.dart'; +import 'package:carg/views/widgets/error_message_widget.dart'; +import 'package:carg/views/widgets/players/player_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; + +class ShareUserScreen extends StatelessWidget { + final Player player; + final AbstractPlayerService playerService; + + const ShareUserScreen( + {super.key, required this.player, required this.playerService}); + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: const Size.fromHeight(60), + child: AppBar( + backgroundColor: Theme.of(context).primaryColor, + foregroundColor: Theme.of(context).colorScheme.onPrimary, + title: Text(AppLocalizations.of(context)!.share, + style: CustomTextStyle.screenHeadLine1(context)), + ), + ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(15.0), + child: Text( + AppLocalizations.of(context)!.playerSelection, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + Flexible( + child: FutureBuilder>( + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(), + ); + } + if (snapshot.connectionState == ConnectionState.none) { + return Container( + alignment: Alignment.center, + child: const Icon( + Icons.error, + ), + ); + } + if (snapshot.data != null) { + return ListView.builder( + padding: const EdgeInsets.all(10), + itemCount: snapshot.data!.length, + itemBuilder: (BuildContext context, int index) { + return ChangeNotifierProvider.value( + value: snapshot.data![index], + child: Consumer( + builder: (context, playerData, child) => PlayerWidget( + player: playerData, + onTap: () => player.sharePlayer(playerData), + ), + ), + ); + }, + ); + } else { + return ErrorMessageWidget( + message: AppLocalizations.of(context)!.noPlayerYet); + } + }, + future: playerService.searchPlayers( + currentPlayer: + Provider.of(context, listen: false) + .getPlayer(), + myPlayers: false), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ChangeNotifierProvider.value( + value: player, + child: Consumer( + builder: (context, playersData, child) => SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Theme.of(context).primaryColor, + ), + foregroundColor: MaterialStateProperty.all( + Theme.of(context).cardColor, + ), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + CustomProperties.borderRadius, + ), + ), + ), + ), + onPressed: () async => { + await playerService.update(player), + Navigator.of(context).pop() + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + AppLocalizations.of(context)!.validate, + style: const TextStyle( + fontSize: 23, + ), + ), + const SizedBox( + width: 10, + ), + const Icon( + Icons.check, + size: 30, + ) + ], + ), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/tabs/player_list_tab.dart b/lib/views/tabs/player_list_tab.dart index 975d1cc4..1e5e87d5 100644 --- a/lib/views/tabs/player_list_tab.dart +++ b/lib/views/tabs/player_list_tab.dart @@ -22,7 +22,6 @@ class PlayerListTab extends StatefulWidget { } class _PlayerListTabWidget extends State { - String? _errorMessage; String searchQuery = ''; late bool isAdmin; final TextEditingController textEditingController = TextEditingController(); @@ -98,7 +97,7 @@ class _PlayerListTabWidget extends State { } if (snapshot.connectionState == ConnectionState.none || snapshot.data == null) { - return ErrorMessageWidget(message: _errorMessage); + return ErrorMessageWidget(message: snapshot.error.toString()); } if (snapshot.data!.isEmpty) { return Center( diff --git a/lib/views/widgets/players/player_widget.dart b/lib/views/widgets/players/player_widget.dart index 13934383..7261b250 100644 --- a/lib/views/widgets/players/player_widget.dart +++ b/lib/views/widgets/players/player_widget.dart @@ -14,11 +14,13 @@ class PlayerWidget extends StatelessWidget { Future _showEditPlayerDialog(BuildContext context) async { var result = await showDialog( - context: context, - builder: (BuildContext context) => PlayerInfoDialog( - player: player, - playerService: PlayerService(), - isNewPlayer: false)); + context: context, + builder: (BuildContext context) => PlayerInfoDialog( + player: player, + playerService: PlayerService(), + isNewPlayer: false, + ), + ); if (result != null) { InfoSnackBar.showSnackBar(context, result); } @@ -27,93 +29,119 @@ class PlayerWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: ElevatedButton( - onPressed: () => - onTap == null ? _showEditPlayerDialog(context) : onTap!(), - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - Theme.of(context).cardColor), - foregroundColor: MaterialStateProperty.all(Colors.black), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - CustomProperties.borderRadius), - side: player.selected - ? BorderSide( - width: 2, color: player.getSideColor(context)) - : BorderSide.none)), - padding: MaterialStateProperty.all( - const EdgeInsets.only(right: 0, left: 15))), - child: SizedBox( - height: 60, - child: - Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - player.profilePicture != '' - ? Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - border: Border.all( - width: 2, - color: player.getSideColor(context), - ), - shape: BoxShape.circle, - image: DecorationImage( - fit: BoxFit.fill, - image: NetworkImage(player.profilePicture, - scale: 1)))), - ) - : const SizedBox(), - Flexible( - flex: 7, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Text( - player.userName, - textAlign: TextAlign.left, - overflow: TextOverflow.ellipsis, - style: const TextStyle(fontSize: 22), - ), - ) - ])), - Flexible( - flex: 2, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - children: [ - const Icon(FontAwesomeIcons.trophy, size: 13), - Text( - ' ${player.totalWonGames()}', - style: const TextStyle(fontSize: 16), - ), - ], - ), - const SizedBox( - height: 5, + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: ElevatedButton( + onPressed: () => + onTap == null ? _showEditPlayerDialog(context) : onTap!(), + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(Theme.of(context).cardColor), + foregroundColor: MaterialStateProperty.all(Colors.black), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(CustomProperties.borderRadius), + side: player.selected + ? BorderSide(width: 2, color: player.getSideColor(context)) + : BorderSide.none, + ), + ), + padding: MaterialStateProperty.all( + const EdgeInsets.only( + right: 0, + left: 15, + ), + ), + ), + child: SizedBox( + height: 60, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + player.profilePicture != '' + ? Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + border: Border.all( + width: 2, + color: player.getSideColor(context), + ), + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.fill, + image: NetworkImage( + player.profilePicture, + scale: 1, ), - Row(children: [ - const Icon(FontAwesomeIcons.gamepad, size: 13), - Text( - ' ${player.totalPlayedGames()}', - style: const TextStyle(fontSize: 16), - ) - ]) - ])), - Container( - width: 15, - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(20), - bottomRight: Radius.circular(20), ), - color: player.getSideColor(context))) - ])))); + ), + ), + ) + : const SizedBox(), + Flexible( + flex: 7, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + player.userName, + textAlign: TextAlign.left, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 22), + ), + ) + ], + ), + ), + Flexible( + flex: 2, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + children: [ + const Icon(FontAwesomeIcons.trophy, size: 13), + Text( + ' ${player.totalWonGames()}', + style: const TextStyle(fontSize: 16), + ), + ], + ), + const SizedBox( + height: 5, + ), + Row( + children: [ + const Icon(FontAwesomeIcons.gamepad, size: 13), + Text( + ' ${player.totalPlayedGames()}', + style: const TextStyle(fontSize: 16), + ) + ], + ) + ], + ), + ), + Container( + width: 15, + decoration: BoxDecoration( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(20), + bottomRight: Radius.circular(20), + ), + color: player.getSideColor( + context, + ), + ), + ) + ], + ), + ), + ), + ); } }