eliminar e tentativa de partilhar
This commit is contained in:
@@ -79,7 +79,19 @@ class GameController {
|
||||
print("Erro ao criar jogo: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// ELIMINAR JOGO
|
||||
Future<bool> deleteGame(String gameId) async {
|
||||
try {
|
||||
await _supabase.from('games').delete().eq('id', gameId);
|
||||
// Como o Supabase tem Cascade Delete (se configurado), vai apagar também
|
||||
// as stats e shot_locations associadas a este game_id automaticamente.
|
||||
return true;
|
||||
} catch (e) {
|
||||
print("Erro ao eliminar jogo: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void dispose() {}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class PlacarController extends ChangeNotifier {
|
||||
String? pendingPlayerId;
|
||||
List<ShotRecord> matchShots = [];
|
||||
|
||||
// 👇 LISTA PARA O HISTÓRICO (PLAY-BY-PLAY)
|
||||
// Lista para o Histórico de Jogadas
|
||||
List<String> playByPlay = [];
|
||||
|
||||
ValueNotifier<Duration> durationNotifier = ValueNotifier(const Duration(minutes: 10));
|
||||
@@ -113,6 +113,13 @@ class PlacarController extends ChangeNotifier {
|
||||
|
||||
gameWasAlreadyFinished = gameResponse['status'] == 'Terminado';
|
||||
|
||||
// CARREGAR HISTÓRICO DA BASE DE DADOS
|
||||
if (gameResponse['play_by_play'] != null) {
|
||||
playByPlay = List<String>.from(gameResponse['play_by_play']);
|
||||
} else {
|
||||
playByPlay = [];
|
||||
}
|
||||
|
||||
final teamsResponse = await supabase.from('teams').select('id, name').inFilter('name', [myTeam, opponentTeam]);
|
||||
for (var t in teamsResponse) {
|
||||
if (t['name'] == myTeam) myTeamDbId = t['id'];
|
||||
@@ -224,7 +231,7 @@ class PlacarController extends ChangeNotifier {
|
||||
'playerStats': playerStats,
|
||||
'myCourt': myCourt, 'myBench': myBench, 'oppCourt': oppCourt, 'oppBench': oppBench,
|
||||
'matchShots': matchShots.map((s) => s.toJson()).toList(),
|
||||
'playByPlay': playByPlay, // 👇 Guarda o histórico
|
||||
'playByPlay': playByPlay, // Guarda o histórico no telemóvel
|
||||
};
|
||||
await prefs.setString('backup_$gameId', jsonEncode(backupData));
|
||||
} catch (e) {
|
||||
@@ -254,7 +261,7 @@ class PlacarController extends ChangeNotifier {
|
||||
List<dynamic> decodedShots = data['matchShots'];
|
||||
matchShots = decodedShots.map((s) => ShotRecord.fromJson(s)).toList();
|
||||
|
||||
playByPlay = List<String>.from(data['playByPlay'] ?? []); // 👇 Carrega o histórico
|
||||
playByPlay = List<String>.from(data['playByPlay'] ?? []);
|
||||
|
||||
debugPrint("🔄 AUTO-SAVE RECUPERADO COM SUCESSO!");
|
||||
}
|
||||
@@ -346,23 +353,22 @@ class PlacarController extends ChangeNotifier {
|
||||
|
||||
void registerShotFromPopup(BuildContext context, String action, String targetPlayer, String zone, int points, double relativeX, double relativeY) {
|
||||
String playerId = targetPlayer.replaceAll("player_my_", "").replaceAll("player_opp_", "");
|
||||
bool isMyTeam = targetPlayer.startsWith("player_my_");
|
||||
bool isMake = action.startsWith("add_");
|
||||
String name = playerNames[playerId]!;
|
||||
String name = playerNames[playerId] ?? "Jogador";
|
||||
|
||||
if (playerStats.containsKey(playerId)) {
|
||||
playerStats[playerId]!['fga'] = playerStats[playerId]!['fga']! + 1;
|
||||
matchShots.add(ShotRecord(
|
||||
relativeX: relativeX,
|
||||
relativeY: relativeY,
|
||||
isMake: isMake,
|
||||
playerId: playerId,
|
||||
playerName: name,
|
||||
zone: zone,
|
||||
points: points
|
||||
));
|
||||
|
||||
if (isMake) {
|
||||
playerStats[playerId]!['fgm'] = playerStats[playerId]!['fgm']! + 1;
|
||||
playerStats[playerId]!['pts'] = playerStats[playerId]!['pts']! + points;
|
||||
if (isMyTeam) myScore += points; else opponentScore += points;
|
||||
}
|
||||
}
|
||||
String finalAction = isMake ? "add_pts_$points" : "miss_$points";
|
||||
commitStat(finalAction, targetPlayer);
|
||||
|
||||
matchShots.add(ShotRecord(relativeX: relativeX, relativeY: relativeY, isMake: isMake, playerId: playerId, playerName: name, zone: zone, points: points));
|
||||
|
||||
_saveLocalBackup();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -457,7 +463,18 @@ class PlacarController extends ChangeNotifier {
|
||||
else if (action == "miss_2" || action == "miss_3") { stats["fga"] = stats["fga"]! + 1; logText = "falhou lançamento ❌"; }
|
||||
else if (action == "add_orb") { stats["orb"] = stats["orb"]! + 1; stats["rbs"] = stats["rbs"]! + 1; logText = "ganhou ressalto ofensivo 🔄"; }
|
||||
else if (action == "add_drb") { stats["drb"] = stats["drb"]! + 1; stats["rbs"] = stats["rbs"]! + 1; logText = "ganhou ressalto defensivo 🛡️"; }
|
||||
else if (action == "add_ast") { stats["ast"] = stats["ast"]! + 1; logText = "fez uma assistência 🤝"; }
|
||||
else if (action == "add_ast") {
|
||||
stats["ast"] = stats["ast"]! + 1;
|
||||
|
||||
if (playByPlay.isNotEmpty && playByPlay[0].contains("marcou") && !playByPlay[0].contains("Assistência")) {
|
||||
playByPlay[0] = "${playByPlay[0]} (Assistência: $name 🤝)";
|
||||
_saveLocalBackup();
|
||||
notifyListeners();
|
||||
return;
|
||||
} else {
|
||||
logText = "fez uma assistência 🤝";
|
||||
}
|
||||
}
|
||||
else if (action == "add_stl") { stats["stl"] = stats["stl"]! + 1; logText = "roubou a bola 🥷"; }
|
||||
else if (action == "add_tov") { stats["tov"] = stats["tov"]! + 1; logText = "perdeu a bola (turnover) 🤦"; }
|
||||
else if (action == "add_blk") { stats["blk"] = stats["blk"]! + 1; logText = "fez um desarme (bloco) ✋"; }
|
||||
@@ -514,6 +531,7 @@ class PlacarController extends ChangeNotifier {
|
||||
if (mvpScore > maxMvpScore && mvpScore > 0) { maxMvpScore = mvpScore; mvpName = pName; }
|
||||
});
|
||||
|
||||
// ATUALIZA O JOGO COM OS NOVOS ESTADOS E COM O HISTÓRICO DE JOGADAS!
|
||||
await supabase.from('games').update({
|
||||
'my_score': myScore,
|
||||
'opponent_score': opponentScore,
|
||||
@@ -527,6 +545,7 @@ class PlacarController extends ChangeNotifier {
|
||||
'top_rbs_name': topRbsName,
|
||||
'top_def_name': topDefName,
|
||||
'mvp_name': mvpName,
|
||||
'play_by_play': playByPlay, // Envia o histórico para a base de dados
|
||||
}).eq('id', gameId);
|
||||
|
||||
if (isGameFinishedNow && !gameWasAlreadyFinished && myTeamDbId != null && oppTeamDbId != null) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:playmaker/pages/PlacarPage.dart';
|
||||
import 'package:playmaker/classe/theme.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart'; // 👇 A MAGIA DO CACHE AQUI
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import '../controllers/team_controller.dart';
|
||||
import '../controllers/game_controller.dart';
|
||||
import '../models/game_model.dart';
|
||||
@@ -12,16 +12,17 @@ class GameResultCard extends StatelessWidget {
|
||||
final String gameId, myTeam, opponentTeam, myScore, opponentScore, status, season;
|
||||
final String? myTeamLogo, opponentTeamLogo;
|
||||
final double sf;
|
||||
final VoidCallback onDelete;
|
||||
|
||||
const GameResultCard({
|
||||
super.key, required this.gameId, required this.myTeam, required this.opponentTeam,
|
||||
required this.myScore, required this.opponentScore, required this.status, required this.season,
|
||||
this.myTeamLogo, this.opponentTeamLogo, required this.sf,
|
||||
this.myTeamLogo, this.opponentTeamLogo, required this.sf, required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bgColor = Theme.of(context).cardTheme.color;
|
||||
final bgColor = Theme.of(context).cardTheme.color ?? Theme.of(context).colorScheme.surface;
|
||||
final textColor = Theme.of(context).colorScheme.onSurface;
|
||||
|
||||
return Container(
|
||||
@@ -33,7 +34,9 @@ class GameResultCard extends StatelessWidget {
|
||||
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10 * sf)],
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.1)),
|
||||
),
|
||||
child: Row(
|
||||
child: Stack(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: _buildTeamInfo(myTeam, AppTheme.primaryRed, myTeamLogo, sf, textColor)),
|
||||
@@ -41,12 +44,47 @@ class GameResultCard extends StatelessWidget {
|
||||
Expanded(child: _buildTeamInfo(opponentTeam, Colors.grey.shade600, opponentTeamLogo, sf, textColor)),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
top: -10 * sf,
|
||||
right: -10 * sf,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.delete_outline, color: Colors.grey.shade400, size: 22 * sf),
|
||||
splashRadius: 20 * sf,
|
||||
onPressed: () => _showDeleteConfirmation(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showDeleteConfirmation(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15 * sf)),
|
||||
title: Text('Eliminar Jogo', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16 * sf, color: Theme.of(context).colorScheme.onSurface)),
|
||||
content: Text('Tem a certeza que deseja eliminar este jogo? Esta ação apagará todas as estatísticas associadas e não pode ser desfeita.', style: TextStyle(fontSize: 14 * sf, color: Theme.of(context).colorScheme.onSurface)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
child: Text('CANCELAR', style: TextStyle(color: Colors.grey, fontSize: 14 * sf))
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(ctx);
|
||||
onDelete();
|
||||
},
|
||||
child: Text('ELIMINAR', style: TextStyle(color: AppTheme.primaryRed, fontWeight: FontWeight.bold, fontSize: 14 * sf))
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// 👇 AVATAR OTIMIZADO COM CACHE 👇
|
||||
Widget _buildTeamInfo(String name, Color color, String? logoUrl, double sf, Color textColor) {
|
||||
final double avatarSize = 48 * sf; // 2 * radius (24)
|
||||
final double avatarSize = 48 * sf;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
@@ -54,18 +92,14 @@ class GameResultCard extends StatelessWidget {
|
||||
child: Container(
|
||||
width: avatarSize,
|
||||
height: avatarSize,
|
||||
color: color.withOpacity(0.1), // Fundo suave para não ser agressivo
|
||||
color: color.withOpacity(0.1),
|
||||
child: (logoUrl != null && logoUrl.isNotEmpty)
|
||||
? CachedNetworkImage(
|
||||
imageUrl: logoUrl,
|
||||
fit: BoxFit.cover,
|
||||
fadeInDuration: Duration.zero, // Fica instantâneo
|
||||
placeholder: (context, url) => Center(
|
||||
child: Icon(Icons.shield, color: color, size: 24 * sf)
|
||||
),
|
||||
errorWidget: (context, url, error) => Center(
|
||||
child: Icon(Icons.shield, color: color, size: 24 * sf)
|
||||
),
|
||||
fadeInDuration: Duration.zero,
|
||||
placeholder: (context, url) => Center(child: Icon(Icons.shield, color: color, size: 24 * sf)),
|
||||
errorWidget: (context, url, error) => Center(child: Icon(Icons.shield, color: color, size: 24 * sf)),
|
||||
)
|
||||
: Center(child: Icon(Icons.shield, color: color, size: 24 * sf)),
|
||||
),
|
||||
@@ -179,7 +213,6 @@ class _CreateGameDialogManualState extends State<CreateGameDialogManual> {
|
||||
);
|
||||
}
|
||||
|
||||
// 👇 PESQUISA COM CACHE 👇
|
||||
Widget _buildSearch(BuildContext context, String label, TextEditingController controller) {
|
||||
return StreamBuilder<List<Map<String, dynamic>>>(
|
||||
stream: widget.teamController.teamsStream,
|
||||
@@ -304,9 +337,33 @@ class _GamePageState extends State<GamePage> {
|
||||
if (team['name'] == game.opponentTeam) oppLogo = team['image_url'];
|
||||
}
|
||||
return GameResultCard(
|
||||
gameId: game.id, myTeam: game.myTeam, opponentTeam: game.opponentTeam, myScore: game.myScore,
|
||||
opponentScore: game.opponentScore, status: game.status, season: game.season, myTeamLogo: myLogo, opponentTeamLogo: oppLogo,
|
||||
gameId: game.id,
|
||||
myTeam: game.myTeam,
|
||||
opponentTeam: game.opponentTeam,
|
||||
myScore: game.myScore,
|
||||
opponentScore: game.opponentScore,
|
||||
status: game.status,
|
||||
season: game.season,
|
||||
myTeamLogo: myLogo,
|
||||
opponentTeamLogo: oppLogo,
|
||||
sf: context.sf,
|
||||
onDelete: () async {
|
||||
bool success = await gameController.deleteGame(game.id);
|
||||
if (context.mounted) {
|
||||
if (success) {
|
||||
// 👇 ISTO FORÇA A LISTA A ATUALIZAR IMEDIATAMENTE 👇
|
||||
setState(() {});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Jogo eliminado com sucesso!'), backgroundColor: Colors.green)
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Erro ao eliminar o jogo.'), backgroundColor: Colors.red)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
final _supabase = Supabase.instance.client;
|
||||
|
||||
String? _avatarUrl;
|
||||
bool _isMemoryLoaded = false; // 👈 A variável mágica que impede o "piscar" inicial
|
||||
bool _isMemoryLoaded = false; // A variável mágica que impede o "piscar" inicial
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -39,7 +39,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
_loadUserAvatar();
|
||||
}
|
||||
|
||||
// 👇 FUNÇÃO OTIMIZADA: Carrega da memória instantaneamente e atualiza em background
|
||||
// FUNÇÃO OTIMIZADA: Carrega da memória instantaneamente e atualiza em background
|
||||
Future<void> _loadUserAvatar() async {
|
||||
// 1. LÊ DA MEMÓRIA RÁPIDA PRIMEIRO
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
@@ -107,7 +107,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
);
|
||||
_loadUserAvatar();
|
||||
},
|
||||
// 👇 SÓ MOSTRA A IMAGEM OU O BONECO DEPOIS DE LER A MEMÓRIA
|
||||
// SÓ MOSTRA A IMAGEM OU O BONECO DEPOIS DE LER A MEMÓRIA
|
||||
child: !_isMemoryLoaded
|
||||
// Nos primeiros 0.05 segs, mostra só o círculo de fundo (sem boneco)
|
||||
? CircleAvatar(backgroundColor: Colors.white.withOpacity(0.2))
|
||||
@@ -167,8 +167,13 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
return StreamBuilder<List<Map<String, dynamic>>>(
|
||||
stream: _teamController.teamsStream,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator()));
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) return SizedBox(height: 200 * context.sf, child: Center(child: Text("Nenhuma equipa criada.", style: TextStyle(color: Theme.of(context).colorScheme.onSurface))));
|
||||
// Correção: Verifica hasData para evitar piscar tela de loading
|
||||
if (!snapshot.hasData && snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator()));
|
||||
}
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return SizedBox(height: 200 * context.sf, child: Center(child: Text("Nenhuma equipa criada.", style: TextStyle(color: Theme.of(context).colorScheme.onSurface))));
|
||||
}
|
||||
|
||||
final teams = snapshot.data!;
|
||||
return ListView.builder(
|
||||
@@ -178,11 +183,11 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
final team = teams[index];
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.shield, color: AppTheme.primaryRed),
|
||||
title: Text(team['name'], style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold)),
|
||||
title: Text(team['name'] ?? 'Sem Nome', style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold)),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedTeamId = team['id'].toString();
|
||||
_selectedTeamName = team['name'];
|
||||
_selectedTeamName = team['name'] ?? 'Desconhecido';
|
||||
_teamWins = int.tryParse(team['wins']?.toString() ?? '0') ?? 0;
|
||||
_teamLosses = int.tryParse(team['losses']?.toString() ?? '0') ?? 0;
|
||||
_teamDraws = int.tryParse(team['draws']?.toString() ?? '0') ?? 0;
|
||||
@@ -325,7 +330,11 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
stream: _supabase.from('games').stream(primaryKey: ['id']).order('game_date', ascending: false),
|
||||
builder: (context, gameSnapshot) {
|
||||
if (gameSnapshot.hasError) return Text("Erro: ${gameSnapshot.error}", style: const TextStyle(color: Colors.red));
|
||||
if (gameSnapshot.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator());
|
||||
|
||||
// Correção: Verifica hasData em vez de ConnectionState para manter a lista na tela enquanto atualiza em plano de fundo
|
||||
if (!gameSnapshot.hasData && gameSnapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
final todosOsJogos = gameSnapshot.data ?? [];
|
||||
final gamesList = todosOsJogos.where((game) {
|
||||
@@ -349,8 +358,8 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
children: gamesList.map((game) {
|
||||
String dbMyTeam = game['my_team']?.toString() ?? '';
|
||||
String dbOppTeam = game['opponent_team']?.toString() ?? '';
|
||||
int dbMyScore = int.tryParse(game['my_score'].toString()) ?? 0;
|
||||
int dbOppScore = int.tryParse(game['opponent_score'].toString()) ?? 0;
|
||||
int dbMyScore = int.tryParse(game['my_score']?.toString() ?? '0') ?? 0;
|
||||
int dbOppScore = int.tryParse(game['opponent_score']?.toString() ?? '0') ?? 0;
|
||||
|
||||
String opponent; int myScore; int oppScore;
|
||||
|
||||
@@ -389,16 +398,33 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
Map<String, dynamic> _calculateLeaders(List<Map<String, dynamic>> data) {
|
||||
Map<String, int> ptsMap = {}; Map<String, int> astMap = {}; Map<String, int> rbsMap = {}; Map<String, String> namesMap = {};
|
||||
for (var row in data) {
|
||||
String pid = row['member_id'].toString();
|
||||
String pid = row['member_id']?.toString() ?? "unknown";
|
||||
namesMap[pid] = row['player_name']?.toString() ?? "Desconhecido";
|
||||
ptsMap[pid] = (ptsMap[pid] ?? 0) + (int.tryParse(row['pts']?.toString() ?? '0') ?? 0);
|
||||
astMap[pid] = (astMap[pid] ?? 0) + (int.tryParse(row['ast']?.toString() ?? '0') ?? 0);
|
||||
rbsMap[pid] = (rbsMap[pid] ?? 0) + (int.tryParse(row['rbs']?.toString() ?? '0') ?? 0);
|
||||
}
|
||||
if (ptsMap.isEmpty) return {'pts_name': '---', 'pts_val': 0, 'ast_name': '---', 'ast_val': 0, 'rbs_name': '---', 'rbs_val': 0};
|
||||
String getBest(Map<String, int> map) { var bestId = map.entries.reduce((a, b) => a.value > b.value ? a : b).key; return namesMap[bestId]!; }
|
||||
int getBestVal(Map<String, int> map) => map.values.reduce((a, b) => a > b ? a : b);
|
||||
return {'pts_name': getBest(ptsMap), 'pts_val': getBestVal(ptsMap), 'ast_name': getBest(astMap), 'ast_val': getBestVal(astMap), 'rbs_name': getBest(rbsMap), 'rbs_val': getBestVal(rbsMap)};
|
||||
|
||||
if (ptsMap.isEmpty) {
|
||||
return {'pts_name': '---', 'pts_val': 0, 'ast_name': '---', 'ast_val': 0, 'rbs_name': '---', 'rbs_val': 0};
|
||||
}
|
||||
|
||||
String getBest(Map<String, int> map) {
|
||||
if (map.isEmpty) return '---';
|
||||
var bestId = map.entries.reduce((a, b) => a.value > b.value ? a : b).key;
|
||||
return namesMap[bestId] ?? '---';
|
||||
}
|
||||
|
||||
int getBestVal(Map<String, int> map) {
|
||||
if (map.isEmpty) return 0;
|
||||
return map.values.reduce((a, b) => a > b ? a : b);
|
||||
}
|
||||
|
||||
return {
|
||||
'pts_name': getBest(ptsMap), 'pts_val': getBestVal(ptsMap),
|
||||
'ast_name': getBest(astMap), 'ast_val': getBestVal(astMap),
|
||||
'rbs_name': getBest(rbsMap), 'rbs_val': getBestVal(rbsMap)
|
||||
};
|
||||
}
|
||||
|
||||
Widget _buildStatCard({required BuildContext context, required String title, required String playerName, required String statValue, required String statLabel, required Color color, bool isHighlighted = false}) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
Reference in New Issue
Block a user