import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; // NOVA CLASSE PARA GUARDAR OS LANÇAMENTOS NO CAMPO class ShotRecord { final Offset position; final bool isMake; ShotRecord(this.position, this.isMake); } class PlacarPage extends StatefulWidget { final String gameId, myTeam, opponentTeam; const PlacarPage({super.key, required this.gameId, required this.myTeam, required this.opponentTeam}); @override State createState() => _PlacarPageState(); } class _PlacarPageState extends State { int _myScore = 0; int _opponentScore = 0; int _myFouls = 0; int _opponentFouls = 0; int _currentQuarter = 1; int _myTimeoutsUsed = 0; int _opponentTimeoutsUsed = 0; List _myCourt = ["Russell", "Reaves", "Davis", "James", "Hachimura"]; List _myBench = ["Reddish", "Wood", "Hayes", "Prince", "Christie"]; List _oppCourt = ["Kyle", "Serge", "Kawhi", "Danny", "Fred"]; List _oppBench = ["Gasol", "Ibaka", "Siakam", "Lowry", "Powell"]; bool _showMyBench = false; bool _showOppBench = false; // --- VARIÁVEIS PARA O MAPA DE LANÇAMENTOS --- bool _isSelectingShotLocation = false; String? _pendingAction; String? _pendingPlayer; List _matchShots = []; // Guarda as marcas na quadra final Map _playerNumbers = { "Russell": "1", "Reaves": "15", "Davis": "3", "James": "6", "Hachimura": "28", "Reddish": "5", "Wood": "35", "Hayes": "11", "Prince": "12", "Christie": "10", "Kyle": "7", "Serge": "9", "Kawhi": "2", "Danny": "14", "Fred": "23", "Gasol": "33", "Ibaka": "25", "Siakam": "43", "Lowry": "7", "Powell": "24", }; final Map> _playerStats = { "Russell": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Reaves": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Davis": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "James": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Hachimura": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Reddish": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Wood": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Hayes": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Prince": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Christie": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Kyle": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Serge": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Kawhi": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Danny": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Fred": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Gasol": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Ibaka": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Siakam": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Lowry": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, "Powell": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0, "fls": 0, "fgm": 0, "fga": 0}, }; Duration _duration = const Duration(minutes: 10); Timer? _timer; bool _isRunning = false; @override void initState() { super.initState(); SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeRight, DeviceOrientation.landscapeLeft]); } void _toggleTimer() { if (_isRunning) { _timer?.cancel(); } else { _timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { if (_duration.inSeconds > 0) { _duration -= const Duration(seconds: 1); } else { _timer?.cancel(); _isRunning = false; if (_currentQuarter < 4) { _currentQuarter++; _duration = const Duration(minutes: 10); _myFouls = 0; _opponentFouls = 0; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Período $_currentQuarter iniciado. Faltas de equipa resetadas!'), backgroundColor: Colors.blue), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('FIM DO JOGO!'), backgroundColor: Colors.red), ); } } }); }); } setState(() => _isRunning = !_isRunning); } void _useTimeout(bool isOpponent) { setState(() { if (isOpponent) { if (_opponentTimeoutsUsed < 3) _opponentTimeoutsUsed++; } else { if (_myTimeoutsUsed < 3) _myTimeoutsUsed++; } _isRunning = false; _timer?.cancel(); }); } String _formatTime(Duration d) => "${d.inMinutes.toString().padLeft(2, '0')}:${d.inSeconds.remainder(60).toString().padLeft(2, '0')}"; @override void dispose() { _timer?.cancel(); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); super.dispose(); } // --- 1. INTERCETAR A AÇÃO PARA VER SE PRECISA DE LOCALIZAÇÃO --- void _handleActionDrag(String action, String playerData) { bool isOpponent = playerData.startsWith("player_opp_"); String name = playerData.replaceAll("player_my_", "").replaceAll("player_opp_", ""); final stats = _playerStats[name]!; if (stats["fls"]! >= 5 && action != "sub_foul") { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('🛑 $name atingiu 5 faltas e está expulso!'), backgroundColor: Colors.red), ); return; } // Se for 2 Pts, 3 Pts, Miss 2 ou Miss 3 -> Abre o mapa para marcar local! if (action == "add_pts_2" || action == "add_pts_3" || action == "miss_2" || action == "miss_3") { setState(() { _pendingAction = action; _pendingPlayer = playerData; _isSelectingShotLocation = true; // Oculta a UI e pede o clique na tela }); } else { // Outras estatísticas (Lances Livres, Ressaltos, Faltas) aplicam direto _commitStat(action, playerData); } } // --- 2. SALVAR A POSIÇÃO DO CLIQUE NA QUADRA --- void _registerShotLocation(Offset position) { setState(() { // Guarda a bolinha no mapa (Verde para pts, Vermelha para miss) bool isMake = _pendingAction!.startsWith("add_pts_"); _matchShots.add(ShotRecord(position, isMake)); // Aplica a estatística de facto _commitStat(_pendingAction!, _pendingPlayer!); // Restaura a tela _isSelectingShotLocation = false; _pendingAction = null; _pendingPlayer = null; }); } // CANCELAR A ESCOLHA DE LOCALIZAÇÃO (Caso o user se arrependa) void _cancelShotLocation() { setState(() { _isSelectingShotLocation = false; _pendingAction = null; _pendingPlayer = null; }); } // --- 3. APLICAR A ESTATÍSTICA FINAL --- void _commitStat(String action, String playerData) { bool isOpponent = playerData.startsWith("player_opp_"); String name = playerData.replaceAll("player_my_", "").replaceAll("player_opp_", ""); final stats = _playerStats[name]!; setState(() { if (action.startsWith("add_pts_")) { int pts = int.parse(action.split("_").last); if (isOpponent) _opponentScore += pts; else _myScore += pts; stats["pts"] = stats["pts"]! + pts; if (pts == 2 || pts == 3) { stats["fgm"] = stats["fgm"]! + 1; stats["fga"] = stats["fga"]! + 1; } } else if (action.startsWith("sub_pts_")) { int pts = int.parse(action.split("_").last); if (isOpponent) { _opponentScore = (_opponentScore - pts < 0) ? 0 : _opponentScore - pts; } else { _myScore = (_myScore - pts < 0) ? 0 : _myScore - pts; } stats["pts"] = (stats["pts"]! - pts < 0) ? 0 : stats["pts"]! - pts; if (pts == 2 || pts == 3) { if (stats["fgm"]! > 0) stats["fgm"] = stats["fgm"]! - 1; if (stats["fga"]! > 0) stats["fga"] = stats["fga"]! - 1; } } else if (action == "miss_2" || action == "miss_3") { stats["fga"] = stats["fga"]! + 1; } else if (action == "add_rbs") { stats["rbs"] = stats["rbs"]! + 1; } else if (action == "add_ast") { stats["ast"] = stats["ast"]! + 1; } else if (action == "add_stl") { stats["stl"] = stats["stl"]! + 1; } else if (action == "add_tov") { stats["tov"] = stats["tov"]! + 1; } else if (action == "add_blk") { stats["blk"] = stats["blk"]! + 1; } else if (action == "add_foul") { stats["fls"] = stats["fls"]! + 1; if (isOpponent) { _opponentFouls++; } else { _myFouls++; } } else if (action == "sub_foul") { if (stats["fls"]! > 0) stats["fls"] = stats["fls"]! - 1; if (isOpponent) { if (_opponentFouls > 0) _opponentFouls--; } else { if (_myFouls > 0) _myFouls--; } } }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFF266174), body: Stack( children: [ Container( margin: const EdgeInsets.only(left: 60, right: 60, bottom: 50), decoration: BoxDecoration(border: Border.all(color: Colors.white, width: 2.0)), child: LayoutBuilder( builder: (context, constraints) { final innerWidth = constraints.maxWidth; final innerHeight = constraints.maxHeight; return Stack( children: [ // GESTURE DETECTOR ABRANGE TODA A QUADRA PARA RECEBER O CLIQUE GestureDetector( onTapDown: (details) { if (_isSelectingShotLocation) { _registerShotLocation(details.localPosition); } }, child: Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage('assets/campo.png'), fit: BoxFit.cover, alignment: Alignment(0.0, 0.2) ), ), // DESENHA AS BOLINHAS DE LANÇAMENTO NA QUADRA child: Stack( children: _matchShots.map((shot) => Positioned( left: shot.position.dx - 8, // Centraliza a bolinha top: shot.position.dy - 8, child: CircleAvatar( radius: 8, backgroundColor: shot.isMake ? Colors.green : Colors.red, child: Icon(shot.isMake ? Icons.check : Icons.close, size: 10, color: Colors.white), ), )).toList(), ), ), ), // --- MODO NORMAL DE JOGO --- if (!_isSelectingShotLocation) ..._buildTacticalFormation(innerWidth, innerHeight), if (!_isSelectingShotLocation) Positioned( top: innerHeight * 0.26, left: innerWidth * 0.40, child: _dragAndTargetBtn("F", Colors.orange, "add_foul", icon: Icons.sports), ), if (!_isSelectingShotLocation) Positioned( top: innerHeight * 0.26, right: innerWidth * 0.40, child: _dragAndTargetBtn("F", Colors.orange, "sub_foul", icon: Icons.block), ), if (!_isSelectingShotLocation) Positioned( top: (innerHeight * 0.30) + 70, left: 0, right: 0, child: Center( child: GestureDetector( onTap: _toggleTimer, child: CircleAvatar( radius: 60, backgroundColor: Colors.grey.withOpacity(0.5), child: Icon(_isRunning ? Icons.pause : Icons.play_arrow, color: Colors.white, size: 50), ), ), ), ), Positioned(top: 0, left: 0, right: 0, child: Center(child: _buildTopScoreboard())), if (!_isSelectingShotLocation) Positioned(bottom: 10, left: 0, right: 0, child: _buildActionButtonsPanel()), // --- MODO SELEÇÃO DE LOCALIZAÇÃO DO LANÇAMENTO --- if (_isSelectingShotLocation) Positioned( top: innerHeight * 0.4, left: 0, right: 0, child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.white) ), child: const Text( "TOQUE NO CAMPO PARA MARCAR O LOCAL DO LANÇAMENTO", style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold), ), ), ), ), if (_isSelectingShotLocation) Positioned( bottom: 20, right: 20, child: FloatingActionButton.extended( onPressed: _cancelShotLocation, backgroundColor: Colors.red, icon: const Icon(Icons.cancel, color: Colors.white), label: const Text("Cancelar", style: TextStyle(color: Colors.white)), ), ) ], ); }, ), ), // BOTÕES LATERAIS E BANCO OCULTOS DURANTE A SELEÇÃO DA QUADRA if (!_isSelectingShotLocation) Positioned( top: 20, left: 10, child: FloatingActionButton( heroTag: 'btn_save', backgroundColor: const Color(0xFF16202C), mini: true, onPressed: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Jogo Guardado!'))), child: const Icon(Icons.save, color: Colors.white), ), ), if (!_isSelectingShotLocation) Positioned( top: 70, left: 10, child: FloatingActionButton( heroTag: 'btn_exit', backgroundColor: const Color(0xFFD92C2C), mini: true, onPressed: () => Navigator.pop(context), child: const Icon(Icons.exit_to_app, color: Colors.white), ), ), if (!_isSelectingShotLocation) Positioned( bottom: 50, left: 10, child: Column( mainAxisSize: MainAxisSize.min, children: [ if (_showMyBench) ..._buildBenchPlayers(_myBench, false), const SizedBox(height: 10), FloatingActionButton( heroTag: 'btn_sub_home', backgroundColor: const Color(0xFF1E5BB2), mini: true, onPressed: () => setState(() => _showMyBench = !_showMyBench), child: const Icon(Icons.swap_horiz, color: Colors.white), ), ], ), ), if (!_isSelectingShotLocation) Positioned( bottom: 50, right: 10, child: Column( mainAxisSize: MainAxisSize.min, children: [ if (_showOppBench) ..._buildBenchPlayers(_oppBench, true), const SizedBox(height: 10), FloatingActionButton( heroTag: 'btn_sub_away', backgroundColor: const Color(0xFFD92C2C), mini: true, onPressed: () => setState(() => _showOppBench = !_showOppBench), child: const Icon(Icons.swap_horiz, color: Colors.white), ), ], ), ), ], ), ); } List _buildTacticalFormation(double w, double h) { return [ Positioned(top: h * 0.25, left: w * 0.02, child: _buildPlayerCard(_playerNumbers[_myCourt[0]]!, _myCourt[0], false)), Positioned(top: h * 0.68, left: w * 0.02, child: _buildPlayerCard(_playerNumbers[_myCourt[1]]!, _myCourt[1], false)), Positioned(top: h * 0.45, left: w * 0.25, child: _buildPlayerCard(_playerNumbers[_myCourt[2]]!, _myCourt[2], false)), Positioned(top: h * 0.15, left: w * 0.20, child: _buildPlayerCard(_playerNumbers[_myCourt[3]]!, _myCourt[3], false)), Positioned(top: h * 0.80, left: w * 0.20, child: _buildPlayerCard(_playerNumbers[_myCourt[4]]!, _myCourt[4], false)), Positioned(top: h * 0.25, right: w * 0.02, child: _buildPlayerCard(_playerNumbers[_oppCourt[0]]!, _oppCourt[0], true)), Positioned(top: h * 0.68, right: w * 0.02, child: _buildPlayerCard(_playerNumbers[_oppCourt[1]]!, _oppCourt[1], true)), Positioned(top: h * 0.45, right: w * 0.25, child: _buildPlayerCard(_playerNumbers[_oppCourt[2]]!, _oppCourt[2], true)), Positioned(top: h * 0.15, right: w * 0.20, child: _buildPlayerCard(_playerNumbers[_oppCourt[3]]!, _oppCourt[3], true)), Positioned(top: h * 0.80, right: w * 0.20, child: _buildPlayerCard(_playerNumbers[_oppCourt[4]]!, _oppCourt[4], true)), ]; } List _buildBenchPlayers(List bench, bool isOpponent) { final teamColor = isOpponent ? const Color(0xFFD92C2C) : const Color(0xFF1E5BB2); // CORREÇÃO: Utilização do prefixo 'bench_' em vez de 'sub_' final prefix = isOpponent ? "bench_opp_" : "bench_my_"; return bench.map((playerName) { final num = _playerNumbers[playerName]!; final int fouls = _playerStats[playerName]!["fls"]!; final bool isFouledOut = fouls >= 5; Widget avatarUI = Container( margin: const EdgeInsets.only(bottom: 5), child: CircleAvatar( backgroundColor: isFouledOut ? Colors.grey.shade700 : teamColor, child: Text( num, style: TextStyle( color: isFouledOut ? Colors.red.shade300 : Colors.white, fontSize: 14, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none ) ), ), ); if (isFouledOut) { return GestureDetector( onTap: () => ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('🛑 $playerName não pode voltar ao jogo (Expulso com 5 faltas).'), backgroundColor: Colors.red), ), child: avatarUI, ); } return Draggable( data: "$prefix$playerName", feedback: Material( color: Colors.transparent, child: CircleAvatar(backgroundColor: teamColor, child: Text(num, style: const TextStyle(color: Colors.white))), ), childWhenDragging: const Opacity(opacity: 0.5, child: SizedBox(width: 40, height: 40)), child: avatarUI, ); }).toList(); } Widget _buildPlayerCard(String number, String name, bool isOpponent) { final teamColor = isOpponent ? const Color(0xFFD92C2C) : const Color(0xFF1E5BB2); final stats = _playerStats[name]!; final prefix = isOpponent ? "player_opp_" : "player_my_"; return Draggable( data: "$prefix$name", 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)), ), ), childWhenDragging: Opacity(opacity: 0.5, child: _playerCardUI(number, name, stats, teamColor, false, false)), child: DragTarget( onAcceptWithDetails: (details) { final action = details.data; if (action.startsWith("add_") || action.startsWith("sub_") || action.startsWith("miss_")) { _handleActionDrag(action, "$prefix$name"); } // CORREÇÃO: Nova lógica que processa apenas ações que comecem por 'bench_' para substituições else if (action.startsWith("bench_")) { setState(() { if (action.startsWith("bench_my_") && !isOpponent) { String benchPlayer = action.replaceAll("bench_my_", ""); if (_playerStats[benchPlayer]!["fls"]! >= 5) return; int courtIndex = _myCourt.indexOf(name); int benchIndex = _myBench.indexOf(benchPlayer); _myCourt[courtIndex] = benchPlayer; _myBench[benchIndex] = name; _showMyBench = false; ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Sai $name, Entra $benchPlayer'))); } if (action.startsWith("bench_opp_") && isOpponent) { String benchPlayer = action.replaceAll("bench_opp_", ""); if (_playerStats[benchPlayer]!["fls"]! >= 5) return; int courtIndex = _oppCourt.indexOf(name); int benchIndex = _oppBench.indexOf(benchPlayer); _oppCourt[courtIndex] = benchPlayer; _oppBench[benchIndex] = name; _showOppBench = false; ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Sai $name, Entra $benchPlayer'))); } }); } }, builder: (context, candidateData, rejectedData) { // CORREÇÃO: Atualização da verificação de hover com base no novo prefixo bool isSubbing = candidateData.any((data) => data != null && (data.startsWith("bench_my_") || data.startsWith("bench_opp_"))); bool isActionHover = candidateData.any((data) => data != null && (data.startsWith("add_") || data.startsWith("sub_") || data.startsWith("miss_"))); return _playerCardUI(number, name, stats, teamColor, isSubbing, isActionHover); }, ), ); } Widget _playerCardUI(String number, String name, Map stats, Color teamColor, bool isSubbing, bool isActionHover) { bool isFouledOut = stats["fls"]! >= 5; Color bgColor = isFouledOut ? Colors.red.shade100 : Colors.white; Color borderColor = isFouledOut ? Colors.redAccent : Colors.transparent; if (isSubbing) { bgColor = Colors.blue.shade50; borderColor = Colors.blue; } else if (isActionHover && !isFouledOut) { bgColor = Colors.orange.shade50; borderColor = Colors.orange; } int fgm = stats["fgm"]!; int fga = stats["fga"]!; String fgPercent = fga > 0 ? ((fgm / fga) * 100).toStringAsFixed(0) : "0"; return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), 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))], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 40, height: 40, decoration: BoxDecoration(color: isFouledOut ? Colors.grey : teamColor, borderRadius: BorderRadius.circular(8)), alignment: Alignment.center, child: Text(number, style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold)), ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( name, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: isFouledOut ? Colors.red : Colors.black87, decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none ) ), const SizedBox(height: 1), Text( "${stats["pts"]} Pts | FG: $fgm/$fga ($fgPercent%)", style: TextStyle(fontSize: 11, color: isFouledOut ? Colors.red : Colors.grey[700], fontWeight: FontWeight.w600) ), Text( "${stats["ast"]} Ast | ${stats["rbs"]} Rbs | ${stats["fls"]} Fls", style: TextStyle(fontSize: 11, color: isFouledOut ? Colors.red : Colors.grey, fontWeight: FontWeight.w500) ), ], ), ], ), ); } Widget _buildTopScoreboard() { return Container( padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30), decoration: BoxDecoration( color: const Color(0xFF16202C), borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(15), bottomRight: Radius.circular(15)), border: Border.all(color: Colors.white, width: 2), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ _buildTeamSection(widget.myTeam, _myScore, _myFouls, _myTimeoutsUsed, const Color(0xFF1E5BB2), false), const SizedBox(width: 25), Column( children: [ _timeDisplay(), const SizedBox(height: 5), Text("PERÍODO $_currentQuarter", style: const TextStyle(color: Colors.orangeAccent, fontSize: 14, fontWeight: FontWeight.bold)), ], ), const SizedBox(width: 25), _buildTeamSection(widget.opponentTeam, _opponentScore, _opponentFouls, _opponentTimeoutsUsed, const Color(0xFFD92C2C), true), ], ), ); } Widget _buildTeamSection(String name, int score, int fouls, int timeouts, Color color, bool isOpp) { final timeoutIndicators = Row( mainAxisSize: MainAxisSize.min, children: List.generate(3, (index) => Container( margin: const EdgeInsets.symmetric(horizontal: 3), width: 12, height: 12, decoration: BoxDecoration(shape: BoxShape.circle, color: index < timeouts ? Colors.yellow : Colors.grey.shade600, border: Border.all(color: Colors.black26)), )), ); return Row( crossAxisAlignment: CrossAxisAlignment.start, children: isOpp ? [ Column(children: [_scoreBox(score, color), const SizedBox(height: 4), Text("FALTAS: $fouls", style: TextStyle(color: fouls >= 5 ? Colors.red : Colors.yellowAccent, fontSize: 12, fontWeight: FontWeight.bold)), timeoutIndicators]), const SizedBox(width: 15), Text(name.toUpperCase(), style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)) ] : [ Text(name.toUpperCase(), style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(width: 15), Column(children: [_scoreBox(score, color), const SizedBox(height: 4), Text("FALTAS: $fouls", style: TextStyle(color: fouls >= 5 ? Colors.red : Colors.yellowAccent, fontSize: 12, fontWeight: FontWeight.bold)), timeoutIndicators]) ] ); } Widget _timeDisplay() => Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6), decoration: BoxDecoration(color: const Color(0xFF2C3E50), borderRadius: BorderRadius.circular(6)), child: Text(_formatTime(_duration), style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, fontFamily: 'monospace')), ); Widget _scoreBox(int score, Color color) => Container( width: 50, height: 40, alignment: Alignment.center, decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(6)), child: Text(score.toString(), style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold)), ); Widget _buildActionButtonsPanel() { return Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ // COLUNA 1: 1 Ponto _columnBtn([ _actionBtn("T.O", const Color(0xFF1E5BB2), () => _useTimeout(false), labelSize: 20), _dragAndTargetBtn("1", Colors.orange, "add_pts_1"), _dragAndTargetBtn("1", Colors.orange, "sub_pts_1", isX: true), _dragAndTargetBtn("AST", Colors.blueGrey, "add_ast"), ]), const SizedBox(width: 15), // COLUNA 2: 2 Pontos _columnBtn([ _dragAndTargetBtn("M2", Colors.redAccent, "miss_2"), _dragAndTargetBtn("2", Colors.orange, "add_pts_2"), _dragAndTargetBtn("2", Colors.orange, "sub_pts_2", isX: true), _dragAndTargetBtn("STL", Colors.green, "add_stl"), ]), const SizedBox(width: 15), // COLUNA 3: 3 Pontos _columnBtn([ _dragAndTargetBtn("M3", Colors.redAccent, "miss_3"), _dragAndTargetBtn("3", Colors.orange, "add_pts_3"), _dragAndTargetBtn("3", Colors.orange, "sub_pts_3", isX: true), _dragAndTargetBtn("TOV", Colors.redAccent, "add_tov"), ]), const SizedBox(width: 15), // COLUNA 4: Outras Stats _columnBtn([ _actionBtn("T.O", const Color(0xFFD92C2C), () => _useTimeout(true), labelSize: 20), _dragAndTargetBtn("ORB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball), _dragAndTargetBtn("DRB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball), // AQUI ESTÁ O BLK COM ÍCONE DE MÃO _dragAndTargetBtn("BLK", Colors.deepPurple, "add_blk", icon: Icons.front_hand), ]), ], ); } Widget _columnBtn(List children) => Column(mainAxisSize: MainAxisSize.min, children: children.map((c) => Padding(padding: const EdgeInsets.only(bottom: 8), child: c)).toList()); Widget _dragAndTargetBtn(String label, Color color, String actionData, {IconData? icon, bool isX = false}) { return Draggable( data: actionData, feedback: _circle(label, color, icon, true, isX: isX), childWhenDragging: Opacity(opacity: 0.5, child: _circle(label, color, icon, false, isX: isX)), child: DragTarget( onAcceptWithDetails: (details) { final playerData = details.data; if (playerData.startsWith("player_")) { _handleActionDrag(actionData, playerData); } }, builder: (context, candidateData, rejectedData) { bool isHovered = candidateData.any((data) => data != null && data.startsWith("player_")); return Transform.scale( scale: isHovered ? 1.15 : 1.0, child: Container( decoration: isHovered ? BoxDecoration(shape: BoxShape.circle, boxShadow: [BoxShadow(color: Colors.white, blurRadius: 10, spreadRadius: 3)]) : null, child: _circle(label, color, icon, false, isX: isX) ), ); } ), ); } Widget _actionBtn(String label, Color color, VoidCallback onTap, {IconData? icon, bool isX = false, double labelSize = 24}) { return GestureDetector(onTap: onTap, child: _circle(label, color, icon, false, fontSize: labelSize, isX: isX)); } Widget _circle(String label, Color color, IconData? icon, bool isFeed, {double fontSize = 20, bool isX = false}) { Widget content; bool isPointBtn = label == "1" || label == "2" || label == "3"; bool isMissBtn = label == "M2" || label == "M3"; bool isBlkBtn = label == "BLK"; // --- DESIGN SIMPLIFICADO: BOLA COM LINHAS PRETAS E NÚMERO --- if (isPointBtn || isMissBtn) { content = Stack( alignment: Alignment.center, children: [ // 1. CÍRCULO SÓLIDO PRETO: Isto preenche as partes "transparentes" do ícone com preto! Container( width: isFeed ? 55 : 45, // Tamanho exato para não vazar pelas bordas da bola height: isFeed ? 55 : 45, decoration: const BoxDecoration( color: Colors.black, // O preto sólido das linhas shape: BoxShape.circle, ), ), // 2. Ícone da Bola de Basquete (Laranja para marcar, avermelhado para falhar) Icon( Icons.sports_basketball, color: color, // Usa a cor laranja ou vermelha passada no botão size: isFeed ? 65 : 55 ), // 3. Número no centro (Preto com contorno branco) Stack( children: [ // Contorno Branco Text( label, style: TextStyle( fontSize: isFeed ? 26 : 22, fontWeight: FontWeight.w900, foreground: Paint() ..style = PaintingStyle.stroke ..strokeWidth = 3 ..color = Colors.white, decoration: TextDecoration.none, ), ), // Texto Preto Text( label, style: TextStyle( fontSize: isFeed ? 26 : 22, fontWeight: FontWeight.w900, color: Colors.black, decoration: TextDecoration.none, ), ), ], ), ], ); } // --- DESIGN DE MÃO COM TEXTO PARA O BLK --- else if (isBlkBtn) { content = Stack( alignment: Alignment.center, children: [ Icon( Icons.front_hand, color: const Color.fromARGB(207, 56, 52, 52), size: isFeed ? 55 : 45 ), Stack( alignment: Alignment.center, children: [ Text( label, style: TextStyle( fontSize: isFeed ? 18 : 16, fontWeight: FontWeight.w900, foreground: Paint() ..style = PaintingStyle.stroke ..strokeWidth = 3 ..color = Colors.black, decoration: TextDecoration.none, ), ), Text( label, style: TextStyle( fontSize: isFeed ? 18 : 16, fontWeight: FontWeight.w900, color: Colors.white, decoration: TextDecoration.none, ), ), ], ), ], ); } // --- RESTANTES BOTÕES DO SISTEMA --- else if (icon != null) { content = Icon(icon, color: Colors.white, size: 30); } else { content = Text(label, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: fontSize, decoration: TextDecoration.none)); } return Stack( clipBehavior: Clip.none, alignment: Alignment.bottomRight, children: [ Container( width: isFeed ? 70 : 60, height: isFeed ? 70 : 60, decoration: (isPointBtn || isMissBtn || isBlkBtn) ? const BoxDecoration(color: Colors.transparent) // Retira o círculo de fundo base : BoxDecoration( gradient: RadialGradient(colors: [color.withOpacity(0.7), color], radius: 0.8), shape: BoxShape.circle, boxShadow: const [BoxShadow(color: Colors.black38, blurRadius: 6, offset: Offset(0, 3))] ), alignment: Alignment.center, child: content, ), // Ícone de Anular if (isX) Positioned( top: 0, right: 0, child: Container( decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle), child: Icon(Icons.cancel, color: Colors.red, size: isFeed ? 28 : 24) ), ), ], ); } }