From 309a2f98bc5635bbcb174d5c189ed51496c615aa Mon Sep 17 00:00:00 2001 From: 230404 <230404@epvc.pt> Date: Thu, 23 Apr 2026 16:13:21 +0100 Subject: [PATCH] melhorar a camisola --- lib/pages/PlacarPage.dart | 385 ++++++++++++-------------------- lib/widgets/placar_widgets.dart | 121 +++++----- 2 files changed, 195 insertions(+), 311 deletions(-) diff --git a/lib/pages/PlacarPage.dart b/lib/pages/PlacarPage.dart index de86ed2..25c47ec 100644 --- a/lib/pages/PlacarPage.dart +++ b/lib/pages/PlacarPage.dart @@ -645,6 +645,76 @@ void showAssistDialog(BuildContext context, PlacarController controller, bool is ); } +// ============================================================================ +// SHIRT PAINTER — Desenho 100% código (Formato Jersey Realista) +// ============================================================================ +class ShirtPainter extends CustomPainter { + final Color color; + final bool isFouledOut; + const ShirtPainter({required this.color, this.isFouledOut = false}); + + @override + void paint(Canvas canvas, Size size) { + final double w = size.width; + final double h = size.height; + final Color shirtColor = isFouledOut ? Colors.grey.shade700 : color; + + // Tinta para preencher a cor da camisola + final paint = Paint() + ..color = shirtColor + ..style = PaintingStyle.fill; + + // Tinta para fazer a borda branca (tipo o acabamento do tecido) + final trimPaint = Paint() + ..color = Colors.white + ..style = PaintingStyle.stroke + ..strokeWidth = w * 0.04 + ..strokeJoin = StrokeJoin.round; + + final path = Path(); + + // 1. Ombro esquerdo (lado do pescoço) + path.moveTo(w * 0.32, h * 0.10); + + // 2. Ombro esquerdo (lado do braço) + path.lineTo(w * 0.18, h * 0.10); + + // 3. Cava do braço esquerdo (curva funda) + path.quadraticBezierTo(w * 0.28, h * 0.35, w * 0.05, h * 0.55); + + // 4. Lado esquerdo (desce até baixo) + path.lineTo(w * 0.15, h * 1.1); + + // 5. Fundo da camisola (linha reta em baixo) + path.lineTo(w * 0.85, h * 1.1); + + // 6. Lado direito (sobe até à axila) + path.lineTo(w * 0.95, h * 0.55); + + // 7. Cava do braço direito (curva funda) + path.quadraticBezierTo(w * 0.72, h * 0.35, w * 0.82, h * 0.10); + + // 8. Ombro direito (lado do braço até ao pescoço) + path.lineTo(w * 0.68, h * 0.10); + + // 9. Gola (decote redondo profundo) + path.quadraticBezierTo(w * 0.50, h * 0.45, w * 0.32, h * 0.10); + + path.close(); + + // Desenha o fundo da cor da equipa + canvas.drawPath(path, paint); + + // Desenha a borda branca por cima para dar estilo + canvas.drawPath(path, trimPaint); + } + + @override + bool shouldRepaint(ShirtPainter old) => old.color != color || old.isFouledOut != isFouledOut; +} +// ============================================================================ +// CARD DO JOGADOR NO CAMPO +// ============================================================================ class PlayerCourtCard extends StatelessWidget { final PlacarController controller; final String playerId; @@ -680,6 +750,7 @@ class PlayerCourtCard extends StatelessWidget { if (action == "add_pts_2" || action == "add_pts_3" || action == "miss_2" || action == "miss_3") { bool isMake = action.startsWith("add_"); bool is3Pt = action.endsWith("_3"); + showDialog( context: context, builder: (ctx) => ZoneMapDialog( @@ -689,58 +760,14 @@ class PlayerCourtCard extends StatelessWidget { onZoneSelected: (zone, points, relX, relY) { Navigator.pop(ctx); controller.registerShotFromPopup(context, action, "$prefix$playerId", zone, points, relX, relY); - if (isMake) showAssistDialog(context, controller, isOpponent, playerId, sf); + + if (isMake) { + showAssistDialog(context, controller, isOpponent, playerId, sf); + } }, ), ); } - else if (action == "add_tov") { - showDialog(context: context, builder: (ctx) => ActionSubtypeDialog( - title: "Escolha o tipo de turnover", - options: { - "add_3s": "3\nsegundos", - "add_24s": "Relógio de\nlançamento\n(24s)", - "add_pa": "Passos", - "add_dr": "Drible duplo", - "add_tov": "Passe ruim", - }, - onSelected: (val) { Navigator.pop(ctx); controller.commitStat(val, "$prefix$playerId"); }, - sf: sf, - )); - } - else if (action == "add_stl") { - showDialog(context: context, builder: (ctx) => ActionSubtypeDialog( - title: "Ação Defensiva", - options: {"add_stl": "Roubo de Bola\n(BR)", "add_il": "Interceção\nLançamento (IL)"}, - onSelected: (val) { Navigator.pop(ctx); controller.commitStat(val, "$prefix$playerId"); }, - sf: sf, - )); - } - else if (action == "add_blk") { - showDialog(context: context, builder: (ctx) => ActionSubtypeDialog( - title: "Ação de Desarme / Bloco", - options: {"add_blk": "Fez o Desarme\n(BLK)", "add_li": "Sofreu o Desarme\n(LI)"}, - onSelected: (val) { Navigator.pop(ctx); controller.commitStat(val, "$prefix$playerId"); }, - sf: sf, - )); - } - else if (action == "add_foul") { - showDialog(context: context, builder: (ctx) => ActionSubtypeDialog( - title: "Escolha o tipo de falta pessoal", - options: { - "Defensiva": "Falta\ndefensiva", - "Ofensiva": "Falta\nofensiva", - "Técnica": "Falta\ntécnica", - "Antidesportiva": "Falta\nantidesportiva", - "Desqualificante": "Falta\ndesqualificante" - }, - onSelected: (foulType) { - Navigator.pop(ctx); - showFoulVictimDialog(context, controller, isOpponent, playerId, foulType, sf); - }, - sf: sf, - )); - } else if (action.startsWith("add_") || action.startsWith("sub_") || action.startsWith("miss_")) { controller.handleActionDrag(context, action, "$prefix$playerId"); } @@ -770,208 +797,68 @@ class PlayerCourtCard extends StatelessWidget { String fgPercent = fga > 0 ? ((fgm / fga) * 100).toStringAsFixed(0) : "0"; String displayName = displayNameStr.length > 12 ? "${displayNameStr.substring(0, 10)}..." : displayNameStr; + // Tamanho da camisola ajustado para ficar perfeito no cartão + final double shirtSize = 40 * sf; + return Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), - decoration: BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(8), border: Border.all(color: borderColor, width: 1.5), boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4, offset: Offset(0, 2))]), - child: ClipRRect( - borderRadius: BorderRadius.circular(6 * sf), - child: IntrinsicHeight( - child: Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 10 * sf), - color: isFouledOut ? Colors.grey[700] : teamColor, + padding: EdgeInsets.symmetric(horizontal: 8 * sf, vertical: 6 * sf), + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(8 * sf), + border: Border.all(color: borderColor, width: 1.5 * sf), + boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4 * sf, offset: Offset(0, 2 * sf))], + ), + child: IntrinsicHeight( + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, // Centra verticalmente a camisola com o texto + children: [ + // ── APENAS A CAMISOLA (Sem quadrado de fundo) ── + SizedBox( + width: shirtSize, + height: shirtSize, + child: Stack( alignment: Alignment.center, - child: Text(number, style: TextStyle(color: Colors.white, fontSize: 18 * sf, fontWeight: FontWeight.bold)), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 8 * sf, vertical: 4 * sf), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text(displayName, style: TextStyle(fontSize: 14 * sf, fontWeight: FontWeight.bold, color: isFouledOut ? AppTheme.actionMiss : Colors.black87, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none)), - SizedBox(height: 1.5 * sf), - Text("${stats["pts"]} Pts | FG: $fgm/$fga ($fgPercent%)", style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[700], fontWeight: FontWeight.w600)), - Text("${stats["ast"]} Ast | ${stats["orb"]! + stats["drb"]!} Rbs | ${stats["fls"]} Fls", style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[500], fontWeight: FontWeight.w600)), - ], - ), - ), - ], - ), - ), - ), - ); - } -} - -class TopScoreboard extends StatelessWidget { - final PlacarController controller; - final double sf; - - const TopScoreboard({super.key, required this.controller, required this.sf}); - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.symmetric(vertical: 6 * sf, horizontal: 20 * sf), - decoration: BoxDecoration( - color: AppTheme.placarDarkSurface, - borderRadius: BorderRadius.only(bottomLeft: Radius.circular(22 * sf), bottomRight: Radius.circular(22 * 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), - SizedBox(width: 20 * sf), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 14 * sf, vertical: 4 * sf), - decoration: BoxDecoration(color: AppTheme.placarTimerBg, borderRadius: BorderRadius.circular(9 * sf)), - child: ValueListenableBuilder( - valueListenable: controller.durationNotifier, - builder: (context, duration, child) { - String formatTime = "${duration.inMinutes.toString().padLeft(2, '0')}:${duration.inSeconds.remainder(60).toString().padLeft(2, '0')}"; - return Text(formatTime, style: TextStyle(color: Colors.white, fontSize: 24 * sf, fontWeight: FontWeight.w900, fontFamily: 'monospace', letterSpacing: 1.5 * sf)); - } - ), - ), - SizedBox(height: 4 * sf), - Text("PERÍODO ${controller.currentQuarter}", style: TextStyle(color: AppTheme.warningAmber, fontSize: 12 * sf, fontWeight: FontWeight.w900)), - ], - ), - SizedBox(width: 20 * sf), - _buildTeamSection(controller.opponentTeam, controller.opponentScore, controller.opponentFouls, controller.opponentTimeoutsUsed, AppTheme.oppTeamRed, true, sf), - ], - ), - ); - } - - Widget _buildTeamSection(String name, int score, int fouls, int timeouts, Color color, bool isOpp, double sf) { - int displayFouls = fouls > 5 ? 5 : fouls; - final timeoutIndicators = Row( - mainAxisSize: MainAxisSize.min, - children: List.generate(3, (index) => Container( - margin: EdgeInsets.symmetric(horizontal: 2.5 * sf), width: 10 * sf, height: 10 * sf, - decoration: BoxDecoration(shape: BoxShape.circle, color: index < timeouts ? AppTheme.warningAmber : Colors.grey.shade600, border: Border.all(color: Colors.white54, width: 1.0 * sf)), - )), - ); - List content = [ - Column(children: [_scoreBox(score, color, sf), SizedBox(height: 5 * sf), timeoutIndicators]), - SizedBox(width: 12 * sf), - Column( - crossAxisAlignment: isOpp ? CrossAxisAlignment.start : CrossAxisAlignment.end, - children: [ - SizedBox( - width: 130 * sf, - child: Text( - name.toUpperCase(), - style: TextStyle(color: Colors.white, fontSize: 16 * sf, fontWeight: FontWeight.w900, letterSpacing: 1.0 * sf), - overflow: TextOverflow.ellipsis, - maxLines: 1, - textAlign: isOpp ? TextAlign.left : TextAlign.right, - ), - ), - SizedBox(height: 3 * sf), - Text("FALTAS: $displayFouls", style: TextStyle(color: displayFouls >= 5 ? AppTheme.actionMiss : AppTheme.warningAmber, fontSize: 11 * sf, fontWeight: FontWeight.bold)), - ], - ) - ]; - return Row(crossAxisAlignment: CrossAxisAlignment.center, children: isOpp ? content : content.reversed.toList()); - } - - Widget _scoreBox(int score, Color color, double sf) => Container( - width: 45 * sf, height: 35 * sf, alignment: Alignment.center, - decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(6 * sf)), - child: Text(score.toString(), style: TextStyle(color: Colors.white, fontSize: 20 * sf, fontWeight: FontWeight.w900)), - ); -} - -class BenchPopup extends StatelessWidget { - final PlacarController controller; - final bool isOpponent; - final double sf; - - const BenchPopup({super.key, required this.controller, required this.isOpponent, required this.sf}); - - @override - Widget build(BuildContext context) { - final bench = isOpponent ? controller.oppBench : controller.myBench; - final teamColor = isOpponent ? AppTheme.oppTeamRed : AppTheme.myTeamBlue; - final prefix = isOpponent ? "bench_opp_" : "bench_my_"; - final teamName = isOpponent ? controller.opponentTeam : controller.myTeam; - - return Container( - width: 280 * sf, - padding: EdgeInsets.all(12 * sf), - decoration: BoxDecoration( - color: AppTheme.placarDarkSurface.withOpacity(0.95), - borderRadius: BorderRadius.circular(16 * sf), - border: Border.all(color: teamColor, width: 2 * sf), - boxShadow: [BoxShadow(color: Colors.black54, blurRadius: 10 * sf, spreadRadius: 2 * sf)], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("SUPLENTES: ${teamName.toUpperCase()}", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12 * sf)), - InkWell( - onTap: () { - if (isOpponent) { controller.showOppBench = false; } - else { controller.showMyBench = false; } - controller.notifyListeners(); - }, - child: Icon(Icons.close, color: Colors.white70, size: 20 * sf), - ) - ], - ), - Divider(color: Colors.white24, height: 16 * sf), - - Wrap( - spacing: 12 * sf, - runSpacing: 12 * sf, - alignment: WrapAlignment.center, - children: bench.map((playerId) { - final playerName = controller.playerNames[playerId] ?? "Erro"; - final num = controller.playerNumbers[playerId] ?? "0"; - final int fouls = controller.playerStats[playerId]?["fls"] ?? 0; - final bool isFouledOut = fouls >= 5; - - String shortName = playerName.length > 8 ? "${playerName.substring(0, 7)}." : playerName; - - Widget avatarUI = Column( - mainAxisSize: MainAxisSize.min, children: [ - CircleAvatar( - radius: 20 * sf, - backgroundColor: isFouledOut ? Colors.grey.shade800 : teamColor, - child: Text(num, style: TextStyle(color: isFouledOut ? Colors.red.shade300 : Colors.white, fontSize: 16 * sf, fontWeight: FontWeight.bold, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none)), + CustomPaint( + size: Size(shirtSize, shirtSize), + painter: ShirtPainter( + color: teamColor, + isFouledOut: isFouledOut, + ), + ), + Padding( + padding: EdgeInsets.only(top: shirtSize * 0.15), + child: Text( + number, + style: TextStyle( + color: Colors.white, + fontSize: shirtSize * 0.40, + fontWeight: FontWeight.w900, + decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none, + shadows: const [Shadow(color: Colors.black45, blurRadius: 2, offset: Offset(1, 1))], + ), + ), ), - SizedBox(height: 4 * sf), - Text(shortName, style: TextStyle(color: Colors.white, fontSize: 10 * sf, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis), ], - ); - - if (isFouledOut) { - return GestureDetector(onTap: () => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('🛑 $playerName expulso!'), backgroundColor: AppTheme.actionMiss)), child: avatarUI); - } - - return Draggable( - data: "$prefix$playerId", - feedback: Material(color: Colors.transparent, child: CircleAvatar(radius: 26 * sf, backgroundColor: teamColor, child: Text(num, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18 * sf)))), - childWhenDragging: Opacity(opacity: 0.3, child: avatarUI), - child: avatarUI, - ); - }).toList(), - ), - ], + ), + ), + SizedBox(width: 8 * sf), // Espaço entre a camisola e as estatísticas + + // ── Estatísticas ───────────────────────────────────── + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text(displayName, style: TextStyle(fontSize: 14 * sf, fontWeight: FontWeight.bold, color: isFouledOut ? AppTheme.actionMiss : Colors.black87, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none)), + SizedBox(height: 1.5 * sf), + Text("${stats["pts"]} Pts | FG: $fgm/$fga ($fgPercent%)", style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[700], fontWeight: FontWeight.w600)), + Text("${stats["ast"]} Ast | ${stats["orb"]! + stats["drb"]!} Rbs | ${stats["fls"]} Fls", style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[500], fontWeight: FontWeight.w600)), + ], + ), + ], + ), ), ); } @@ -1286,6 +1173,9 @@ class HeatmapCourtPainter extends CustomPainter { bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } +// ============================================================================ +// 5. CAIXA DE HISTÓRICO (PLAY-BY-PLAY) +// ============================================================================ class PlayByPlayDialog extends StatelessWidget { final PlacarController controller; const PlayByPlayDialog({super.key, required this.controller}); @@ -1333,6 +1223,9 @@ class PlayByPlayDialog extends StatelessWidget { } } +// ============================================================================ +// 6. ECRÃ DE BOX SCORE (ESTATÍSTICAS GERAIS) +// ============================================================================ class BoxScoreDialog extends StatelessWidget { final PlacarController controller; final double sf; @@ -1380,7 +1273,6 @@ class BoxScoreDialog extends StatelessWidget { indicatorColor: AppTheme.warningAmber, labelColor: Colors.white, unselectedLabelColor: Colors.white54, - // 👇 LETRAS DAS ABAS MAIORES labelStyle: TextStyle(fontSize: 14 * sf, fontWeight: FontWeight.bold), indicatorWeight: 3 * sf, dividerColor: Colors.white10, @@ -1394,7 +1286,6 @@ class BoxScoreDialog extends StatelessWidget { child: Container( width: double.infinity, color: Colors.black12, - // 👇 MÁGICA DE PERFORMANCE: Só a zona da tabela é que redesenha por segundo! child: ValueListenableBuilder( valueListenable: controller.durationNotifier, builder: (context, duration, _) { diff --git a/lib/widgets/placar_widgets.dart b/lib/widgets/placar_widgets.dart index 9caf43a..b923da8 100644 --- a/lib/widgets/placar_widgets.dart +++ b/lib/widgets/placar_widgets.dart @@ -257,6 +257,9 @@ class ShirtPainter extends CustomPainter { bool shouldRepaint(ShirtPainter old) => old.color != color || old.isFouledOut != isFouledOut; } +// ============================================================================ +// CARD DO JOGADOR NO CAMPO +// ============================================================================ class PlayerCourtCard extends StatelessWidget { final PlacarController controller; final String playerId; @@ -327,7 +330,7 @@ class PlayerCourtCard extends StatelessWidget { ); } - Widget _playerCardUI(String number, String displayNameStr, Map stats, Color teamColor, bool isSubbing, bool isActionHover, double sf) { +Widget _playerCardUI(String number, String displayNameStr, Map stats, Color teamColor, bool isSubbing, bool isActionHover, double sf) { bool isFouledOut = stats["fls"]! >= 5; Color bgColor = isFouledOut ? Colors.red.shade100 : Colors.white; Color borderColor = isFouledOut ? AppTheme.actionMiss : Colors.transparent; @@ -339,77 +342,67 @@ class PlayerCourtCard extends StatelessWidget { String fgPercent = fga > 0 ? ((fgm / fga) * 100).toStringAsFixed(0) : "0"; String displayName = displayNameStr.length > 12 ? "${displayNameStr.substring(0, 10)}..." : displayNameStr; - // Tamanho da camisola - final double shirtSize = 38 * sf; + // Tamanho da camisola ajustado para ficar perfeito no cartão + final double shirtSize = 42 * sf; return Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), + padding: EdgeInsets.symmetric(horizontal: 8 * sf, vertical: 6 * sf), decoration: BoxDecoration( color: bgColor, - borderRadius: BorderRadius.circular(8), - border: Border.all(color: borderColor, width: 1.5), - boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4, offset: Offset(0, 2))], + borderRadius: BorderRadius.circular(8 * sf), + border: Border.all(color: borderColor, width: 1.5 * sf), + boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4 * sf, offset: Offset(0, 2 * sf))], ), - child: ClipRRect( - borderRadius: BorderRadius.circular(6 * sf), - child: IntrinsicHeight( - child: Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // ── Camisola com número ────────────────────────────── - Container( - padding: EdgeInsets.symmetric(horizontal: 6 * sf, vertical: 4 * sf), - color: isFouledOut ? Colors.grey.shade200 : teamColor.withOpacity(0.12), + child: IntrinsicHeight( + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, // Centra verticalmente a camisola com o texto + children: [ + // ── APENAS A CAMISOLA (Sem quadrado de fundo) ── + SizedBox( + width: shirtSize, + height: shirtSize, + child: Stack( alignment: Alignment.center, - child: SizedBox( - width: shirtSize, - height: shirtSize * 1.1, - child: Stack( - alignment: Alignment.center, - children: [ - // Camisola desenhada - CustomPaint( - size: Size(shirtSize, shirtSize * 1.1), - painter: ShirtPainter( - color: isFouledOut ? Colors.grey.shade600 : teamColor, - isFouledOut: isFouledOut, - ), - ), - // Número por cima - Padding( - padding: EdgeInsets.only(top: shirtSize * 0.15), - child: Text( - number, - style: TextStyle( - color: Colors.white, - fontSize: shirtSize * 0.42, - fontWeight: FontWeight.w900, - decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none, - shadows: const [Shadow(color: Colors.black45, blurRadius: 3)], - ), - ), - ), - ], + children: [ + CustomPaint( + size: Size(shirtSize, shirtSize), + painter: ShirtPainter( + color: teamColor, + isFouledOut: isFouledOut, + ), ), - ), + Padding( + padding: EdgeInsets.only(top: shirtSize * 0.15), + child: Text( + number, + style: TextStyle( + color: Colors.white, + fontSize: shirtSize * 0.40, + fontWeight: FontWeight.w900, + decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none, + shadows: const [Shadow(color: Colors.black45, blurRadius: 2, offset: Offset(1, 1))], + ), + ), + ), + ], ), - // ── Estatísticas ───────────────────────────────────── - Padding( - padding: EdgeInsets.symmetric(horizontal: 8 * sf, vertical: 4 * sf), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text(displayName, style: TextStyle(fontSize: 14 * sf, fontWeight: FontWeight.bold, color: isFouledOut ? AppTheme.actionMiss : Colors.black87, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none)), - SizedBox(height: 1.5 * sf), - Text("${stats["pts"]} Pts | FG: $fgm/$fga ($fgPercent%)", style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[700], fontWeight: FontWeight.w600)), - Text("${stats["ast"]} Ast | ${stats["orb"]! + stats["drb"]!} Rbs | ${stats["fls"]} Fls", style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[500], fontWeight: FontWeight.w600)), - ], - ), - ), - ], - ), + ), + SizedBox(width: 8 * sf), // Espaço entre a camisola e as estatísticas + + // ── Estatísticas ───────────────────────────────────── + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text(displayName, style: TextStyle(fontSize: 14 * sf, fontWeight: FontWeight.bold, color: isFouledOut ? AppTheme.actionMiss : Colors.black87, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none)), + SizedBox(height: 1.5 * sf), + Text("${stats["pts"]} Pts | FG: $fgm/$fga ($fgPercent%)", style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[700], fontWeight: FontWeight.w600)), + Text("${stats["ast"]} Ast | ${stats["orb"]! + stats["drb"]!} Rbs | ${stats["fls"]} Fls", style: TextStyle(fontSize: 10 * sf, color: isFouledOut ? AppTheme.actionMiss : Colors.grey[500], fontWeight: FontWeight.w600)), + ], + ), + ], ), ), );