From 8adea3f7b651f3fabb985ed30cb60f14f283d13f Mon Sep 17 00:00:00 2001 From: 230404 <230404@epvc.pt> Date: Wed, 18 Mar 2026 12:39:03 +0000 Subject: [PATCH] bora vamos --- lib/pages/PlacarPage.dart | 267 ++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 152 deletions(-) diff --git a/lib/pages/PlacarPage.dart b/lib/pages/PlacarPage.dart index c75bbdc..4a943c4 100644 --- a/lib/pages/PlacarPage.dart +++ b/lib/pages/PlacarPage.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:playmaker/controllers/placar_controller.dart'; import 'package:playmaker/utils/size_extension.dart'; -import 'package:playmaker/classe/theme.dart'; // 👇 IMPORT DO TEU TEMA! +import 'package:playmaker/classe/theme.dart'; import 'dart:math' as math; import 'package:playmaker/zone_map_dialog.dart'; // ============================================================================ -// 1. PLACAR SUPERIOR (CRONÓMETRO E RESULTADO) +// 1. PLACAR SUPERIOR (CRONÓMETRO E RESULTADO) - TAMANHO REDUZIDO // ============================================================================ class TopScoreboard extends StatelessWidget { final PlacarController controller; @@ -19,43 +19,44 @@ class TopScoreboard extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.symmetric(vertical: 10 * sf, horizontal: 35 * sf), + // 👇 Reduzido padding vertical e horizontal + padding: EdgeInsets.symmetric(vertical: 6 * sf, horizontal: 20 * sf), decoration: BoxDecoration( - color: AppTheme.placarDarkSurface, // 🎨 USANDO TEMA + color: AppTheme.placarDarkSurface, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(22 * sf), bottomRight: Radius.circular(22 * sf) ), - border: Border.all(color: Colors.white, width: 2.5 * sf), + border: Border.all(color: Colors.white, width: 2.0 * sf), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - _buildTeamSection(controller.myTeam, controller.myScore, controller.myFouls, controller.myTimeoutsUsed, AppTheme.myTeamBlue, false, sf), // 🎨 USANDO TEMA - SizedBox(width: 30 * sf), + _buildTeamSection(controller.myTeam, controller.myScore, controller.myFouls, controller.myTimeoutsUsed, AppTheme.myTeamBlue, false, sf), + SizedBox(width: 20 * sf), // 👇 Reduzido espaçamento central Column( mainAxisSize: MainAxisSize.min, children: [ Container( - padding: EdgeInsets.symmetric(horizontal: 18 * sf, vertical: 5 * sf), + padding: EdgeInsets.symmetric(horizontal: 14 * sf, vertical: 4 * sf), // 👇 Reduzido decoration: BoxDecoration( - color: AppTheme.placarTimerBg, // 🎨 USANDO TEMA + color: AppTheme.placarTimerBg, borderRadius: BorderRadius.circular(9 * sf) ), child: Text( controller.formatTime(), - style: TextStyle(color: Colors.white, fontSize: 28 * sf, fontWeight: FontWeight.w900, fontFamily: 'monospace', letterSpacing: 2 * sf) + style: TextStyle(color: Colors.white, fontSize: 24 * sf, fontWeight: FontWeight.w900, fontFamily: 'monospace', letterSpacing: 1.5 * sf) // 👇 Fonte reduzida ), ), - SizedBox(height: 5 * sf), + SizedBox(height: 4 * sf), Text( "PERÍODO ${controller.currentQuarter}", - style: TextStyle(color: AppTheme.warningAmber, fontSize: 14 * sf, fontWeight: FontWeight.w900) + style: TextStyle(color: AppTheme.warningAmber, fontSize: 12 * sf, fontWeight: FontWeight.w900) // 👇 Fonte reduzida ), ], ), - SizedBox(width: 30 * sf), - _buildTeamSection(controller.opponentTeam, controller.opponentScore, controller.opponentFouls, controller.opponentTimeoutsUsed, AppTheme.oppTeamRed, true, sf), // 🎨 USANDO TEMA + SizedBox(width: 20 * sf), // 👇 Reduzido espaçamento central + _buildTeamSection(controller.opponentTeam, controller.opponentScore, controller.opponentFouls, controller.opponentTimeoutsUsed, AppTheme.oppTeamRed, true, sf), ], ), ); @@ -67,12 +68,12 @@ class TopScoreboard extends StatelessWidget { final timeoutIndicators = Row( mainAxisSize: MainAxisSize.min, children: List.generate(3, (index) => Container( - margin: EdgeInsets.symmetric(horizontal: 3.5 * sf), - width: 12 * sf, height: 12 * sf, + margin: EdgeInsets.symmetric(horizontal: 2.5 * sf), // 👇 Reduzido + width: 10 * sf, height: 10 * sf, // 👇 Bolas de timeout menores decoration: BoxDecoration( shape: BoxShape.circle, color: index < timeouts ? AppTheme.warningAmber : Colors.grey.shade600, - border: Border.all(color: Colors.white54, width: 1.5 * sf) + border: Border.all(color: Colors.white54, width: 1.0 * sf) ), )), ); @@ -81,22 +82,22 @@ class TopScoreboard extends StatelessWidget { Column( children: [ _scoreBox(score, color, sf), - SizedBox(height: 7 * sf), + SizedBox(height: 5 * sf), // 👇 Reduzido timeoutIndicators ] ), - SizedBox(width: 18 * sf), + SizedBox(width: 12 * sf), // 👇 Reduzido Column( crossAxisAlignment: isOpp ? CrossAxisAlignment.start : CrossAxisAlignment.end, children: [ Text( name.toUpperCase(), - style: TextStyle(color: Colors.white, fontSize: 20 * sf, fontWeight: FontWeight.w900, letterSpacing: 1.2 * sf) + style: TextStyle(color: Colors.white, fontSize: 16 * sf, fontWeight: FontWeight.w900, letterSpacing: 1.0 * sf) // 👇 Fonte reduzida ), - SizedBox(height: 5 * sf), + SizedBox(height: 3 * sf), // 👇 Reduzido Text( "FALTAS: $displayFouls", - style: TextStyle(color: displayFouls >= 5 ? AppTheme.actionMiss : AppTheme.warningAmber, fontSize: 13 * sf, fontWeight: FontWeight.bold) + style: TextStyle(color: displayFouls >= 5 ? AppTheme.actionMiss : AppTheme.warningAmber, fontSize: 11 * sf, fontWeight: FontWeight.bold) // 👇 Fonte reduzida ), ], ) @@ -106,15 +107,15 @@ class TopScoreboard extends StatelessWidget { } Widget _scoreBox(int score, Color color, double sf) => Container( - width: 58 * sf, height: 45 * sf, + width: 45 * sf, height: 35 * sf, // 👇 Caixa de pontuação menor alignment: Alignment.center, - decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(7 * sf)), - child: Text(score.toString(), style: TextStyle(color: Colors.white, fontSize: 26 * sf, fontWeight: FontWeight.w900)), + decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(6 * sf)), + child: Text(score.toString(), style: TextStyle(color: Colors.white, fontSize: 20 * sf, fontWeight: FontWeight.w900)), // 👇 Fonte reduzida ); } // ============================================================================ -// 2. BANCO DE SUPLENTES (DRAG & DROP) +// 2. BANCO DE SUPLENTES (DRAG & DROP) - TAMANHO REDUZIDO // ============================================================================ class BenchPlayersList extends StatelessWidget { final PlacarController controller; @@ -126,7 +127,7 @@ class BenchPlayersList extends StatelessWidget { @override Widget build(BuildContext context) { final bench = isOpponent ? controller.oppBench : controller.myBench; - final teamColor = isOpponent ? AppTheme.oppTeamRed : AppTheme.myTeamBlue; // 🎨 TEMA + final teamColor = isOpponent ? AppTheme.oppTeamRed : AppTheme.myTeamBlue; final prefix = isOpponent ? "bench_opp_" : "bench_my_"; return Column( @@ -137,20 +138,20 @@ class BenchPlayersList extends StatelessWidget { final bool isFouledOut = fouls >= 5; Widget avatarUI = Container( - margin: EdgeInsets.only(bottom: 7 * sf), + margin: EdgeInsets.only(bottom: 5 * sf), // 👇 Reduzido decoration: BoxDecoration( shape: BoxShape.circle, - border: Border.all(color: Colors.white, width: 1.8 * sf), - boxShadow: [BoxShadow(color: Colors.black45, blurRadius: 5 * sf, offset: Offset(0, 2.5 * sf))] + border: Border.all(color: Colors.white, width: 1.5 * sf), // 👇 Reduzido + boxShadow: [BoxShadow(color: Colors.black45, blurRadius: 4 * sf, offset: Offset(0, 2.0 * sf))] ), child: CircleAvatar( - radius: 22 * sf, + radius: 18 * sf, // 👇 Avatar do banco menor backgroundColor: isFouledOut ? Colors.grey.shade800 : teamColor, child: Text( num, style: TextStyle( color: isFouledOut ? Colors.red.shade300 : Colors.white, - fontSize: 16 * sf, + fontSize: 14 * sf, // 👇 Fonte reduzida fontWeight: FontWeight.bold, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none ) @@ -170,12 +171,12 @@ class BenchPlayersList extends StatelessWidget { feedback: Material( color: Colors.transparent, child: CircleAvatar( - radius: 28 * sf, + radius: 22 * sf, // 👇 Avatar ao arrastar menor backgroundColor: teamColor, - child: Text(num, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18 * sf)) + child: Text(num, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16 * sf)) ) ), - childWhenDragging: Opacity(opacity: 0.5, child: SizedBox(width: 45 * sf, height: 45 * sf)), + childWhenDragging: Opacity(opacity: 0.5, child: SizedBox(width: 36 * sf, height: 36 * sf)), // 👇 Placeholder menor child: avatarUI, ); }).toList(), @@ -184,7 +185,7 @@ class BenchPlayersList extends StatelessWidget { } // ============================================================================ -// 3. CARTÃO DO JOGADOR NO CAMPO (TARGET DE FALTAS/PONTOS/SUBSTITUIÇÕES) +// 3. CARTÃO DO JOGADOR NO CAMPO - TAMANHO REDUZIDO // ============================================================================ class PlayerCourtCard extends StatelessWidget { final PlacarController controller; @@ -196,7 +197,7 @@ class PlayerCourtCard extends StatelessWidget { @override Widget build(BuildContext context) { - final teamColor = isOpponent ? AppTheme.oppTeamRed : AppTheme.myTeamBlue; // 🎨 TEMA + final teamColor = isOpponent ? AppTheme.oppTeamRed : AppTheme.myTeamBlue; final stats = controller.playerStats[name]!; final number = controller.playerNumbers[name]!; final prefix = isOpponent ? "player_opp_" : "player_my_"; @@ -206,9 +207,9 @@ class PlayerCourtCard extends StatelessWidget { feedback: Material( color: Colors.transparent, child: Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), - decoration: BoxDecoration(color: teamColor.withOpacity(0.9), borderRadius: BorderRadius.circular(8)), - child: Text(name, style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), // 👇 Reduzido + decoration: BoxDecoration(color: teamColor.withOpacity(0.9), borderRadius: BorderRadius.circular(6)), + child: Text(name, style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)), ), ), childWhenDragging: Opacity(opacity: 0.5, child: _playerCardUI(number, name, stats, teamColor, false, false, sf)), @@ -266,42 +267,42 @@ class PlayerCourtCard extends StatelessWidget { String displayName = name.length > 12 ? "${name.substring(0, 10)}..." : name; return Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), // 👇 Reduzido padding do cartão decoration: BoxDecoration( - color: bgColor, borderRadius: BorderRadius.circular(12), border: Border.all(color: borderColor, width: 2), - boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 6, offset: Offset(0, 3))], + color: bgColor, borderRadius: BorderRadius.circular(8), border: Border.all(color: borderColor, width: 1.5), // 👇 Bordas reduzidas + boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4, offset: Offset(0, 2))], ), child: ClipRRect( - borderRadius: BorderRadius.circular(9 * sf), + borderRadius: BorderRadius.circular(6 * sf), child: IntrinsicHeight( child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( - padding: EdgeInsets.symmetric(horizontal: 16 * sf), + padding: EdgeInsets.symmetric(horizontal: 10 * sf), // 👇 Padding do número menor color: isFouledOut ? Colors.grey[700] : teamColor, alignment: Alignment.center, - child: Text(number, style: TextStyle(color: Colors.white, fontSize: 22 * sf, fontWeight: FontWeight.bold)), + child: Text(number, style: TextStyle(color: Colors.white, fontSize: 18 * sf, fontWeight: FontWeight.bold)), // 👇 Fonte do número menor ), Padding( - padding: EdgeInsets.symmetric(horizontal: 12 * sf, vertical: 7 * sf), + padding: EdgeInsets.symmetric(horizontal: 8 * sf, vertical: 4 * sf), // 👇 Padding dos stats menor child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( displayName, - style: TextStyle(fontSize: 16 * sf, fontWeight: FontWeight.bold, color: isFouledOut ? AppTheme.actionMiss : Colors.black87, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none) + style: TextStyle(fontSize: 14 * sf, fontWeight: FontWeight.bold, color: isFouledOut ? AppTheme.actionMiss : Colors.black87, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none) // 👇 Nome menor ), - SizedBox(height: 2.5 * sf), + SizedBox(height: 1.5 * sf), // 👇 Espaçamento menor Text( "${stats["pts"]} Pts | FG: $fgm/$fga ($fgPercent%)", - style: TextStyle(fontSize: 12 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[700], fontWeight: FontWeight.w600) + style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[700], fontWeight: FontWeight.w600) // 👇 Stats menores ), Text( "${stats["ast"]} Ast | ${stats["orb"]! + stats["drb"]!} Rbs | ${stats["fls"]} Fls", - style: TextStyle(fontSize: 12 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[500], fontWeight: FontWeight.w600) + style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[500], fontWeight: FontWeight.w600) // 👇 Stats menores ), ], ), @@ -315,7 +316,7 @@ class PlayerCourtCard extends StatelessWidget { } // ============================================================================ -// 4. PAINEL DE BOTÕES DE AÇÃO (PONTOS, RESSALTOS, ETC) +// 4. PAINEL DE BOTÕES DE AÇÃO - TAMANHO REDUZIDO // ============================================================================ class ActionButtonsPanel extends StatelessWidget { final PlacarController controller; @@ -323,28 +324,29 @@ class ActionButtonsPanel extends StatelessWidget { const ActionButtonsPanel({super.key, required this.controller, required this.sf}); - @override +@override Widget build(BuildContext context) { - final double baseSize = 65 * sf; - final double feedSize = 82 * sf; - final double gap = 7 * sf; + // 👇👇 Ajuste para o "Meio-Termo" ideal 👇👇 + final double baseSize = 58 * sf; // Aumentado de 50 para 58 + final double feedSize = 73 * sf; // Aumentado de 65 para 73 + final double gap = 5 * sf; return Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ _columnBtn([ - _dragAndTargetBtn("M1", AppTheme.actionMiss, "miss_1", baseSize, feedSize, sf), // 🎨 TEMA - _dragAndTargetBtn("1", AppTheme.actionPoints, "add_pts_1", baseSize, feedSize, sf), // 🎨 TEMA + _dragAndTargetBtn("M1", AppTheme.actionMiss, "miss_1", baseSize, feedSize, sf), + _dragAndTargetBtn("1", AppTheme.actionPoints, "add_pts_1", baseSize, feedSize, sf), _dragAndTargetBtn("1", AppTheme.actionPoints, "sub_pts_1", baseSize, feedSize, sf, isX: true), - _dragAndTargetBtn("STL", AppTheme.actionSteal, "add_stl", baseSize, feedSize, sf), // 🎨 TEMA + _dragAndTargetBtn("STL", AppTheme.actionSteal, "add_stl", baseSize, feedSize, sf), ], gap), SizedBox(width: gap * 1), _columnBtn([ _dragAndTargetBtn("M2", AppTheme.actionMiss, "miss_2", baseSize, feedSize, sf), _dragAndTargetBtn("2", AppTheme.actionPoints, "add_pts_2", baseSize, feedSize, sf), _dragAndTargetBtn("2", AppTheme.actionPoints, "sub_pts_2", baseSize, feedSize, sf, isX: true), - _dragAndTargetBtn("AST", AppTheme.actionAssist, "add_ast", baseSize, feedSize, sf), // 🎨 TEMA + _dragAndTargetBtn("AST", AppTheme.actionAssist, "add_ast", baseSize, feedSize, sf), ], gap), SizedBox(width: gap * 1), _columnBtn([ @@ -355,9 +357,9 @@ class ActionButtonsPanel extends StatelessWidget { ], gap), SizedBox(width: gap * 1), _columnBtn([ - _dragAndTargetBtn("ORB", AppTheme.actionRebound, "add_orb", baseSize, feedSize, sf, icon: Icons.sports_basketball), // 🎨 TEMA - _dragAndTargetBtn("DRB", AppTheme.actionRebound, "add_drb", baseSize, feedSize, sf, icon: Icons.sports_basketball), // 🎨 TEMA - _dragAndTargetBtn("BLK", AppTheme.actionBlock, "add_blk", baseSize, feedSize, sf, icon: Icons.front_hand), // 🎨 TEMA + _dragAndTargetBtn("ORB", AppTheme.actionRebound, "add_orb", baseSize, feedSize, sf, icon: Icons.sports_basketball), + _dragAndTargetBtn("DRB", AppTheme.actionRebound, "add_drb", baseSize, feedSize, sf, icon: Icons.sports_basketball), + _dragAndTargetBtn("BLK", AppTheme.actionBlock, "add_blk", baseSize, feedSize, sf, icon: Icons.front_hand), ], gap), ], ); @@ -452,6 +454,9 @@ class ActionButtonsPanel extends StatelessWidget { } } +// ============================================================================ +// 5. PÁGINA DO PLACAR +// ============================================================================ class PlacarPage extends StatefulWidget { final String gameId, myTeam, opponentTeam; @@ -505,20 +510,20 @@ class _PlacarPageState extends State { feedback: Material( color: Colors.transparent, child: CircleAvatar( - radius: 30 * sf, + radius: 25 * sf, // 👇 Botão flutuante de falta menor backgroundColor: color.withOpacity(0.8), - child: Icon(icon, color: Colors.white, size: 30 * sf) + child: Icon(icon, color: Colors.white, size: 25 * sf) ), ), child: Column( children: [ CircleAvatar( - radius: 27 * sf, + radius: 22 * sf, // 👇 Botão flutuante menor backgroundColor: color, - child: Icon(icon, color: Colors.white, size: 28 * sf), + child: Icon(icon, color: Colors.white, size: 22 * sf), ), SizedBox(height: 5 * sf), - Text(label, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12 * sf)), + Text(label, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 10 * sf)), // 👇 Texto menor ], ), ), @@ -532,16 +537,17 @@ class _PlacarPageState extends State { child: FloatingActionButton( heroTag: heroTag, backgroundColor: color, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14 * (size / 50))), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10 * (size / 40))), // 👇 Curvatura ajustada ao novo tamanho elevation: 5, onPressed: isLoading ? null : onTap, child: isLoading - ? SizedBox(width: size * 0.45, height: size * 0.45, child: const CircularProgressIndicator(color: Colors.white, strokeWidth: 2.5)) + ? SizedBox(width: size * 0.45, height: size * 0.45, child: const CircularProgressIndicator(color: Colors.white, strokeWidth: 2.0)) : Icon(icon, color: Colors.white, size: size * 0.55), ), ); } -void _showHeatmap(BuildContext context) { + + void _showHeatmap(BuildContext context) { showDialog( context: context, builder: (ctx) => HeatmapDialog( @@ -559,12 +565,13 @@ void _showHeatmap(BuildContext context) { final double wScreen = MediaQuery.of(context).size.width; final double hScreen = MediaQuery.of(context).size.height; - final double sf = math.min(wScreen / 1150, hScreen / 720); - final double cornerBtnSize = 48 * sf; + // 👇👇 DICA EXTRA APLICADA AQUI: Aumentei o divisor base para que o sf seja menor por defeito + final double sf = math.min(wScreen / 1250, hScreen / 800); + final double cornerBtnSize = 40 * sf; // 👇 Botões dos cantos (Salvar, Mapa, Banco) menores if (_controller.isLoading) { return Scaffold( - backgroundColor: AppTheme.placarDarkSurface, // 🎨 TEMA + backgroundColor: AppTheme.placarDarkSurface, body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -582,7 +589,7 @@ void _showHeatmap(BuildContext context) { "Os jogadores estão a terminar o aquecimento..." ]; String frase = frases[DateTime.now().second % frases.length]; - return Text(frase, style: TextStyle(color: AppTheme.actionPoints.withOpacity(0.7), fontSize: 26 * sf, fontStyle: FontStyle.italic)); // 🎨 TEMA + return Text(frase, style: TextStyle(color: AppTheme.actionPoints.withOpacity(0.7), fontSize: 26 * sf, fontStyle: FontStyle.italic)); }, ), ], @@ -592,7 +599,7 @@ void _showHeatmap(BuildContext context) { } return Scaffold( - backgroundColor: AppTheme.placarBackground, // 🎨 TEMA (Fundo azul bonito) + backgroundColor: AppTheme.placarBackground, body: SafeArea( top: false, bottom: false, @@ -600,9 +607,8 @@ void _showHeatmap(BuildContext context) { ignoring: _controller.isSaving, child: Stack( children: [ - // --- 1. O CAMPO LIMPO --- Container( - margin: EdgeInsets.only(left: 65 * sf, right: 65 * sf, bottom: 55 * sf), + margin: EdgeInsets.only(left: 55 * sf, right: 55 * sf, bottom: 45 * sf), // 👇 Margens reduzidas para dar mais espaço ao campo decoration: BoxDecoration(border: Border.all(color: Colors.white, width: 2.5)), child: LayoutBuilder( builder: (context, constraints) { @@ -642,21 +648,21 @@ void _showHeatmap(BuildContext context) { ], if (!_controller.isSelectingShotLocation) ...[ - _buildFloatingFoulBtn("FALTA +", AppTheme.actionPoints, "add_foul", Icons.sports, w * 0.39, 0.0, h * 0.31, sf), // 🎨 TEMA - _buildFloatingFoulBtn("FALTA -", AppTheme.actionMiss, "sub_foul", Icons.block, 0.0, w * 0.39, h * 0.31, sf), // 🎨 TEMA + _buildFloatingFoulBtn("FALTA +", AppTheme.actionPoints, "add_foul", Icons.sports, w * 0.39, 0.0, h * 0.31, sf), + _buildFloatingFoulBtn("FALTA -", AppTheme.actionMiss, "sub_foul", Icons.block, 0.0, w * 0.39, h * 0.31, sf), ], if (!_controller.isSelectingShotLocation) Positioned( - top: (h * 0.32) + (40 * sf), + top: (h * 0.38) + (30 * sf), // 👇 Ajustado posição left: 0, right: 0, child: Center( child: GestureDetector( onTap: () => _controller.toggleTimer(context), child: CircleAvatar( - radius: 68 * sf, + radius: 60 * sf, // 👇 Botão de play/pause menor backgroundColor: Colors.grey.withOpacity(0.5), - child: Icon(_controller.isRunning ? Icons.pause : Icons.play_arrow, color: Colors.white, size: 58 * sf) + child: Icon(_controller.isRunning ? Icons.pause : Icons.play_arrow, color: Colors.white, size: 50 * sf) ), ), ), @@ -664,16 +670,16 @@ void _showHeatmap(BuildContext context) { Positioned(top: 0, left: 0, right: 0, child: Center(child: TopScoreboard(controller: _controller, sf: sf))), - if (!_controller.isSelectingShotLocation) Positioned(bottom: -10 * sf, left: 0, right: 0, child: ActionButtonsPanel(controller: _controller, sf: sf)), + if (!_controller.isSelectingShotLocation) Positioned(bottom: -5 * sf, left: 0, right: 0, child: ActionButtonsPanel(controller: _controller, sf: sf)), if (_controller.isSelectingShotLocation) Positioned( top: h * 0.4, left: 0, right: 0, child: Center( child: Container( - padding: EdgeInsets.symmetric(horizontal: 35 * sf, vertical: 18 * sf), - decoration: BoxDecoration(color: Colors.black87, borderRadius: BorderRadius.circular(11 * sf), border: Border.all(color: Colors.white, width: 1.5 * sf)), - child: Text("TOQUE NO CAMPO PARA MARCAR O LOCAL DO LANÇAMENTO", style: TextStyle(color: Colors.white, fontSize: 27 * sf, fontWeight: FontWeight.bold)), + padding: EdgeInsets.symmetric(horizontal: 25 * sf, vertical: 12 * sf), // 👇 Aviso menor + decoration: BoxDecoration(color: Colors.black87, borderRadius: BorderRadius.circular(8 * sf), border: Border.all(color: Colors.white, width: 1.5 * sf)), + child: Text("TOQUE NO CAMPO PARA MARCAR O LOCAL DO LANÇAMENTO", style: TextStyle(color: Colors.white, fontSize: 20 * sf, fontWeight: FontWeight.bold)), // 👇 Fonte menor ), ), ), @@ -683,17 +689,12 @@ void _showHeatmap(BuildContext context) { ), ), - // ========================================== - // BOTÕES LATERAIS DE FORA DO CAMPO - // ========================================== - - // Topo Esquerdo: Guardar e Sair Positioned( - top: 50 * sf, left: 12 * sf, + top: 40 * sf, left: 8 * sf, child: _buildCornerBtn( heroTag: 'btn_save_exit', icon: Icons.save_alt, - color: AppTheme.oppTeamRed, // 🎨 TEMA + color: AppTheme.oppTeamRed, size: cornerBtnSize, isLoading: _controller.isSaving, onTap: () async { @@ -705,9 +706,8 @@ void _showHeatmap(BuildContext context) { ), ), - // Topo Direito: Mapa de Calor Positioned( - top: 50 * sf, right: 12 * sf, + top: 40 * sf, right: 8 * sf, child: _buildCornerBtn( heroTag: 'btn_heatmap', icon: Icons.local_fire_department, @@ -717,46 +717,44 @@ void _showHeatmap(BuildContext context) { ), ), - // Base Esquerda: Banco + TIMEOUT DA CASA Positioned( - bottom: 55 * sf, left: 12 * sf, + bottom: 45 * sf, left: 8 * sf, child: Column( mainAxisSize: MainAxisSize.min, children: [ if (_controller.showMyBench) BenchPlayersList(controller: _controller, isOpponent: false, sf: sf), - SizedBox(height: 12 * sf), - _buildCornerBtn(heroTag: 'btn_sub_home', icon: Icons.swap_horiz, color: AppTheme.myTeamBlue, size: cornerBtnSize, onTap: () { _controller.showMyBench = !_controller.showMyBench; _controller.onUpdate(); }), // 🎨 TEMA - SizedBox(height: 12 * sf), + SizedBox(height: 8 * sf), + _buildCornerBtn(heroTag: 'btn_sub_home', icon: Icons.swap_horiz, color: AppTheme.myTeamBlue, size: cornerBtnSize, onTap: () { _controller.showMyBench = !_controller.showMyBench; _controller.onUpdate(); }), + SizedBox(height: 8 * sf), _buildCornerBtn( heroTag: 'btn_to_home', icon: Icons.timer, - color: _controller.myTimeoutsUsed >= 3 ? Colors.grey : AppTheme.myTeamBlue, // 🎨 TEMA + color: _controller.myTimeoutsUsed >= 3 ? Colors.grey : AppTheme.myTeamBlue, size: cornerBtnSize, onTap: _controller.myTimeoutsUsed >= 3 - ? () => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: const Text('🛑 A equipa da casa já usou os 3 Timeouts deste período!'), backgroundColor: AppTheme.actionMiss)) // 🎨 TEMA + ? () => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: const Text('🛑 A equipa da casa já usou os 3 Timeouts deste período!'), backgroundColor: AppTheme.actionMiss)) : () => _controller.useTimeout(false) ), ], ), ), - // Base Direita: Banco + TIMEOUT DO VISITANTE Positioned( - bottom: 55 * sf, right: 12 * sf, + bottom: 45 * sf, right: 8 * sf, child: Column( mainAxisSize: MainAxisSize.min, children: [ if (_controller.showOppBench) BenchPlayersList(controller: _controller, isOpponent: true, sf: sf), - SizedBox(height: 12 * sf), - _buildCornerBtn(heroTag: 'btn_sub_away', icon: Icons.swap_horiz, color: AppTheme.oppTeamRed, size: cornerBtnSize, onTap: () { _controller.showOppBench = !_controller.showOppBench; _controller.onUpdate(); }), // 🎨 TEMA - SizedBox(height: 12 * sf), + SizedBox(height: 8 * sf), + _buildCornerBtn(heroTag: 'btn_sub_away', icon: Icons.swap_horiz, color: AppTheme.oppTeamRed, size: cornerBtnSize, onTap: () { _controller.showOppBench = !_controller.showOppBench; _controller.onUpdate(); }), + SizedBox(height: 8 * sf), _buildCornerBtn( heroTag: 'btn_to_away', icon: Icons.timer, - color: _controller.opponentTimeoutsUsed >= 3 ? Colors.grey : AppTheme.oppTeamRed, // 🎨 TEMA + color: _controller.opponentTimeoutsUsed >= 3 ? Colors.grey : AppTheme.oppTeamRed, size: cornerBtnSize, onTap: _controller.opponentTimeoutsUsed >= 3 - ? () => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: const Text('🛑 A equipa visitante já usou os 3 Timeouts deste período!'), backgroundColor: AppTheme.actionMiss)) // 🎨 TEMA + ? () => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: const Text('🛑 A equipa visitante já usou os 3 Timeouts deste período!'), backgroundColor: AppTheme.actionMiss)) : () => _controller.useTimeout(true) ), ], @@ -778,7 +776,7 @@ void _showHeatmap(BuildContext context) { } // ============================================================================ -// 👇 O TEU NOVO MAPA DE CALOR AVANÇADO (FILTRO POR EQUIPA E JOGADOR) 👇 +// MAPA DE CALOR // ============================================================================ class HeatmapDialog extends StatefulWidget { final List shots; @@ -813,21 +811,14 @@ class _HeatmapDialogState extends State { final double dialogHeight = screenHeight * 0.95; final double dialogWidth = dialogHeight * 1.0; - // 1. DEFINIR QUAIS BOLINHAS APARECEM CONFORME OS FILTROS List filteredShots = widget.shots.where((shot) { - // Filtro de Equipa if (_selectedTeam == widget.myTeamName && !widget.myPlayers.contains(shot.playerName)) return false; if (_selectedTeam == widget.oppTeamName && !widget.oppPlayers.contains(shot.playerName)) return false; - - // Filtro de Jogador if (_selectedPlayer != 'Todos os Jogadores' && shot.playerName != _selectedPlayer) return false; - return true; }).toList(); - // 2. DEFINIR AS OPÇÕES DOS DROPDOWNS List teamOptions = ['Ambas as Equipas', widget.myTeamName, widget.oppTeamName]; - List playerOptions = ['Todos os Jogadores']; Set activePlayers = widget.shots.map((s) => s.playerName).toSet(); @@ -839,7 +830,6 @@ class _HeatmapDialogState extends State { playerOptions.addAll(activePlayers.where((p) => widget.oppPlayers.contains(p))); } - // Se a equipa mudar e o jogador antigo não pertencer à equipa nova, reseta para "Todos" if (!playerOptions.contains(_selectedPlayer)) { _selectedPlayer = 'Todos os Jogadores'; } @@ -854,21 +844,15 @@ class _HeatmapDialogState extends State { width: dialogWidth, child: Column( children: [ - // CABEÇALHO COM OS DOIS FILTROS Container( - height: 50, // Aumentei um pouco para caber bem os dropdowns + height: 50, color: headerColor, width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( children: [ - const Text( - "📊 Mapa de Calor", - style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), - ), + const Text("📊 Mapa de Calor", style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)), const Spacer(), - - // 👇 FILTRO DE EQUIPA 👇 Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration(color: Colors.black12, borderRadius: BorderRadius.circular(6)), @@ -879,23 +863,15 @@ class _HeatmapDialogState extends State { icon: const Icon(Icons.arrow_drop_down, color: Colors.white), style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13), items: teamOptions.map((String team) { - return DropdownMenuItem( - value: team, - child: Text(team), - ); + return DropdownMenuItem(value: team, child: Text(team)); }).toList(), onChanged: (String? newValue) { - setState(() { - _selectedTeam = newValue!; - }); + setState(() { _selectedTeam = newValue!; }); }, ), ), ), - const SizedBox(width: 10), - - // 👇 FILTRO DE JOGADOR 👇 Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration(color: Colors.black12, borderRadius: BorderRadius.circular(6)), @@ -906,22 +882,15 @@ class _HeatmapDialogState extends State { icon: const Icon(Icons.arrow_drop_down, color: Colors.white), style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13), items: playerOptions.map((String player) { - return DropdownMenuItem( - value: player, - child: Text(player), - ); + return DropdownMenuItem(value: player, child: Text(player)); }).toList(), onChanged: (String? newValue) { - setState(() { - _selectedPlayer = newValue!; - }); + setState(() { _selectedPlayer = newValue!; }); }, ), ), ), - const SizedBox(width: 15), - InkWell( onTap: () => Navigator.pop(context), child: Container( @@ -933,20 +902,15 @@ class _HeatmapDialogState extends State { ], ), ), - - // O CAMPO DESENHADO Expanded( child: LayoutBuilder( builder: (context, constraints) { return Stack( children: [ - // O Desenho das Linhas do Campo (Usamos um pintor simples) CustomPaint( size: Size(constraints.maxWidth, constraints.maxHeight), painter: HeatmapCourtPainter(), ), - - // As Bolinhas por cima do desenho ...filteredShots.map((shot) => Positioned( left: (shot.relativeX * constraints.maxWidth) - 8, top: (shot.relativeY * constraints.maxHeight) - 8, @@ -968,7 +932,6 @@ class _HeatmapDialogState extends State { } } -// Pintor dedicado apenas a desenhar as linhas (sem lógica de sombras do ZoneMap) class HeatmapCourtPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) {