eliminar e tentativa de partilhar

This commit is contained in:
2026-04-04 01:28:47 +01:00
parent 1b08ed7d07
commit 2544e52636
5 changed files with 169 additions and 56 deletions

View File

@@ -79,7 +79,19 @@ class GameController {
print("Erro ao criar jogo: $e"); print("Erro ao criar jogo: $e");
return null; 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() {} void dispose() {}
} }

View File

@@ -78,7 +78,7 @@ class PlacarController extends ChangeNotifier {
String? pendingPlayerId; String? pendingPlayerId;
List<ShotRecord> matchShots = []; List<ShotRecord> matchShots = [];
// 👇 LISTA PARA O HISTÓRICO (PLAY-BY-PLAY) // Lista para o Histórico de Jogadas
List<String> playByPlay = []; List<String> playByPlay = [];
ValueNotifier<Duration> durationNotifier = ValueNotifier(const Duration(minutes: 10)); ValueNotifier<Duration> durationNotifier = ValueNotifier(const Duration(minutes: 10));
@@ -113,6 +113,13 @@ class PlacarController extends ChangeNotifier {
gameWasAlreadyFinished = gameResponse['status'] == 'Terminado'; 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]); final teamsResponse = await supabase.from('teams').select('id, name').inFilter('name', [myTeam, opponentTeam]);
for (var t in teamsResponse) { for (var t in teamsResponse) {
if (t['name'] == myTeam) myTeamDbId = t['id']; if (t['name'] == myTeam) myTeamDbId = t['id'];
@@ -224,7 +231,7 @@ class PlacarController extends ChangeNotifier {
'playerStats': playerStats, 'playerStats': playerStats,
'myCourt': myCourt, 'myBench': myBench, 'oppCourt': oppCourt, 'oppBench': oppBench, 'myCourt': myCourt, 'myBench': myBench, 'oppCourt': oppCourt, 'oppBench': oppBench,
'matchShots': matchShots.map((s) => s.toJson()).toList(), '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)); await prefs.setString('backup_$gameId', jsonEncode(backupData));
} catch (e) { } catch (e) {
@@ -254,7 +261,7 @@ class PlacarController extends ChangeNotifier {
List<dynamic> decodedShots = data['matchShots']; List<dynamic> decodedShots = data['matchShots'];
matchShots = decodedShots.map((s) => ShotRecord.fromJson(s)).toList(); 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!"); 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) { 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_", ""); String playerId = targetPlayer.replaceAll("player_my_", "").replaceAll("player_opp_", "");
bool isMyTeam = targetPlayer.startsWith("player_my_");
bool isMake = action.startsWith("add_"); bool isMake = action.startsWith("add_");
String name = playerNames[playerId]!; String name = playerNames[playerId] ?? "Jogador";
if (playerStats.containsKey(playerId)) { matchShots.add(ShotRecord(
playerStats[playerId]!['fga'] = playerStats[playerId]!['fga']! + 1; relativeX: relativeX,
relativeY: relativeY,
isMake: isMake,
playerId: playerId,
playerName: name,
zone: zone,
points: points
));
if (isMake) { String finalAction = isMake ? "add_pts_$points" : "miss_$points";
playerStats[playerId]!['fgm'] = playerStats[playerId]!['fgm']! + 1; commitStat(finalAction, targetPlayer);
playerStats[playerId]!['pts'] = playerStats[playerId]!['pts']! + points;
if (isMyTeam) myScore += points; else opponentScore += points;
}
}
matchShots.add(ShotRecord(relativeX: relativeX, relativeY: relativeY, isMake: isMake, playerId: playerId, playerName: name, zone: zone, points: points));
_saveLocalBackup();
notifyListeners(); 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 == "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_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_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_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_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) ✋"; } 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; } 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({ await supabase.from('games').update({
'my_score': myScore, 'my_score': myScore,
'opponent_score': opponentScore, 'opponent_score': opponentScore,
@@ -527,6 +545,7 @@ class PlacarController extends ChangeNotifier {
'top_rbs_name': topRbsName, 'top_rbs_name': topRbsName,
'top_def_name': topDefName, 'top_def_name': topDefName,
'mvp_name': mvpName, 'mvp_name': mvpName,
'play_by_play': playByPlay, // Envia o histórico para a base de dados
}).eq('id', gameId); }).eq('id', gameId);
if (isGameFinishedNow && !gameWasAlreadyFinished && myTeamDbId != null && oppTeamDbId != null) { if (isGameFinishedNow && !gameWasAlreadyFinished && myTeamDbId != null && oppTeamDbId != null) {

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:playmaker/pages/PlacarPage.dart'; import 'package:playmaker/pages/PlacarPage.dart';
import 'package:playmaker/classe/theme.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/team_controller.dart';
import '../controllers/game_controller.dart'; import '../controllers/game_controller.dart';
import '../models/game_model.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 gameId, myTeam, opponentTeam, myScore, opponentScore, status, season;
final String? myTeamLogo, opponentTeamLogo; final String? myTeamLogo, opponentTeamLogo;
final double sf; final double sf;
final VoidCallback onDelete;
const GameResultCard({ const GameResultCard({
super.key, required this.gameId, required this.myTeam, required this.opponentTeam, super.key, required this.gameId, required this.myTeam, required this.opponentTeam,
required this.myScore, required this.opponentScore, required this.status, required this.season, 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 @override
Widget build(BuildContext context) { 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; final textColor = Theme.of(context).colorScheme.onSurface;
return Container( return Container(
@@ -33,20 +34,57 @@ class GameResultCard extends StatelessWidget {
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10 * sf)], boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10 * sf)],
border: Border.all(color: Colors.grey.withOpacity(0.1)), border: Border.all(color: Colors.grey.withOpacity(0.1)),
), ),
child: Row( child: Stack(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded(child: _buildTeamInfo(myTeam, AppTheme.primaryRed, myTeamLogo, sf, textColor)), Row(
_buildScoreCenter(context, gameId, sf, textColor), mainAxisAlignment: MainAxisAlignment.spaceBetween,
Expanded(child: _buildTeamInfo(opponentTeam, Colors.grey.shade600, opponentTeamLogo, sf, textColor)), children: [
Expanded(child: _buildTeamInfo(myTeam, AppTheme.primaryRed, myTeamLogo, sf, textColor)),
_buildScoreCenter(context, gameId, sf, textColor),
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),
),
),
], ],
), ),
); );
} }
// 👇 AVATAR OTIMIZADO COM CACHE 👇 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))
),
],
)
);
}
Widget _buildTeamInfo(String name, Color color, String? logoUrl, double sf, Color textColor) { 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( return Column(
children: [ children: [
@@ -54,18 +92,14 @@ class GameResultCard extends StatelessWidget {
child: Container( child: Container(
width: avatarSize, width: avatarSize,
height: 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) child: (logoUrl != null && logoUrl.isNotEmpty)
? CachedNetworkImage( ? CachedNetworkImage(
imageUrl: logoUrl, imageUrl: logoUrl,
fit: BoxFit.cover, fit: BoxFit.cover,
fadeInDuration: Duration.zero, // Fica instantâneo fadeInDuration: Duration.zero,
placeholder: (context, url) => Center( placeholder: (context, url) => Center(child: Icon(Icons.shield, color: color, size: 24 * sf)),
child: Icon(Icons.shield, color: color, size: 24 * sf) errorWidget: (context, url, error) => 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)), : 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) { Widget _buildSearch(BuildContext context, String label, TextEditingController controller) {
return StreamBuilder<List<Map<String, dynamic>>>( return StreamBuilder<List<Map<String, dynamic>>>(
stream: widget.teamController.teamsStream, stream: widget.teamController.teamsStream,
@@ -304,9 +337,33 @@ class _GamePageState extends State<GamePage> {
if (team['name'] == game.opponentTeam) oppLogo = team['image_url']; if (team['name'] == game.opponentTeam) oppLogo = team['image_url'];
} }
return GameResultCard( return GameResultCard(
gameId: game.id, myTeam: game.myTeam, opponentTeam: game.opponentTeam, myScore: game.myScore, gameId: game.id,
opponentScore: game.opponentScore, status: game.status, season: game.season, myTeamLogo: myLogo, opponentTeamLogo: oppLogo, 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, 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)
);
}
}
},
); );
}, },
); );

View File

@@ -31,7 +31,7 @@ class _HomeScreenState extends State<HomeScreen> {
final _supabase = Supabase.instance.client; final _supabase = Supabase.instance.client;
String? _avatarUrl; 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 @override
void initState() { void initState() {
@@ -39,7 +39,7 @@ class _HomeScreenState extends State<HomeScreen> {
_loadUserAvatar(); _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 { Future<void> _loadUserAvatar() async {
// 1. LÊ DA MEMÓRIA RÁPIDA PRIMEIRO // 1. LÊ DA MEMÓRIA RÁPIDA PRIMEIRO
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
@@ -84,7 +84,7 @@ class _HomeScreenState extends State<HomeScreen> {
final List<Widget> pages = [ final List<Widget> pages = [
_buildHomeContent(context), _buildHomeContent(context),
const GamePage(), const GamePage(),
const TeamsPage(), const TeamsPage(),
const StatusPage(), const StatusPage(),
]; ];
@@ -107,7 +107,7 @@ class _HomeScreenState extends State<HomeScreen> {
); );
_loadUserAvatar(); _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 child: !_isMemoryLoaded
// Nos primeiros 0.05 segs, mostra só o círculo de fundo (sem boneco) // Nos primeiros 0.05 segs, mostra só o círculo de fundo (sem boneco)
? CircleAvatar(backgroundColor: Colors.white.withOpacity(0.2)) ? CircleAvatar(backgroundColor: Colors.white.withOpacity(0.2))
@@ -167,8 +167,13 @@ class _HomeScreenState extends State<HomeScreen> {
return StreamBuilder<List<Map<String, dynamic>>>( return StreamBuilder<List<Map<String, dynamic>>>(
stream: _teamController.teamsStream, stream: _teamController.teamsStream,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())); // Correção: Verifica hasData para evitar piscar tela de loading
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)))); 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!; final teams = snapshot.data!;
return ListView.builder( return ListView.builder(
@@ -178,11 +183,11 @@ class _HomeScreenState extends State<HomeScreen> {
final team = teams[index]; final team = teams[index];
return ListTile( return ListTile(
leading: const Icon(Icons.shield, color: AppTheme.primaryRed), 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: () { onTap: () {
setState(() { setState(() {
_selectedTeamId = team['id'].toString(); _selectedTeamId = team['id'].toString();
_selectedTeamName = team['name']; _selectedTeamName = team['name'] ?? 'Desconhecido';
_teamWins = int.tryParse(team['wins']?.toString() ?? '0') ?? 0; _teamWins = int.tryParse(team['wins']?.toString() ?? '0') ?? 0;
_teamLosses = int.tryParse(team['losses']?.toString() ?? '0') ?? 0; _teamLosses = int.tryParse(team['losses']?.toString() ?? '0') ?? 0;
_teamDraws = int.tryParse(team['draws']?.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), stream: _supabase.from('games').stream(primaryKey: ['id']).order('game_date', ascending: false),
builder: (context, gameSnapshot) { builder: (context, gameSnapshot) {
if (gameSnapshot.hasError) return Text("Erro: ${gameSnapshot.error}", style: const TextStyle(color: Colors.red)); 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 todosOsJogos = gameSnapshot.data ?? [];
final gamesList = todosOsJogos.where((game) { final gamesList = todosOsJogos.where((game) {
@@ -349,8 +358,8 @@ class _HomeScreenState extends State<HomeScreen> {
children: gamesList.map((game) { children: gamesList.map((game) {
String dbMyTeam = game['my_team']?.toString() ?? ''; String dbMyTeam = game['my_team']?.toString() ?? '';
String dbOppTeam = game['opponent_team']?.toString() ?? ''; String dbOppTeam = game['opponent_team']?.toString() ?? '';
int dbMyScore = int.tryParse(game['my_score'].toString()) ?? 0; int dbMyScore = int.tryParse(game['my_score']?.toString() ?? '0') ?? 0;
int dbOppScore = int.tryParse(game['opponent_score'].toString()) ?? 0; int dbOppScore = int.tryParse(game['opponent_score']?.toString() ?? '0') ?? 0;
String opponent; int myScore; int oppScore; 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, dynamic> _calculateLeaders(List<Map<String, dynamic>> data) {
Map<String, int> ptsMap = {}; Map<String, int> astMap = {}; Map<String, int> rbsMap = {}; Map<String, String> namesMap = {}; Map<String, int> ptsMap = {}; Map<String, int> astMap = {}; Map<String, int> rbsMap = {}; Map<String, String> namesMap = {};
for (var row in data) { 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"; namesMap[pid] = row['player_name']?.toString() ?? "Desconhecido";
ptsMap[pid] = (ptsMap[pid] ?? 0) + (int.tryParse(row['pts']?.toString() ?? '0') ?? 0); ptsMap[pid] = (ptsMap[pid] ?? 0) + (int.tryParse(row['pts']?.toString() ?? '0') ?? 0);
astMap[pid] = (astMap[pid] ?? 0) + (int.tryParse(row['ast']?.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); 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]!; } if (ptsMap.isEmpty) {
int getBestVal(Map<String, int> map) => map.values.reduce((a, b) => a > b ? a : b); return {'pts_name': '---', 'pts_val': 0, 'ast_name': '---', 'ast_val': 0, 'rbs_name': '---', 'rbs_val': 0};
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)}; }
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}) { Widget _buildStatCard({required BuildContext context, required String title, required String playerName, required String statValue, required String statLabel, required Color color, bool isHighlighted = false}) {

View File

@@ -1 +0,0 @@
import 'package:flutter/material.dart';