diff --git a/lib/pages/PlacarPage.dart b/lib/pages/PlacarPage.dart index cc35d33..b064d81 100644 --- a/lib/pages/PlacarPage.dart +++ b/lib/pages/PlacarPage.dart @@ -1,322 +1,15 @@ -/*import 'dart:async'; + +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -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(); +// NOVA CLASSE PARA GUARDAR OS LANÇAMENTOS NO CAMPO +class ShotRecord { + final Offset position; + final bool isMake; + ShotRecord(this.position, this.isMake); } -class _PlacarPageState extends State { - int _myScore = 0; - int _opponentScore = 0; - int _myFouls = 0; - int _opponentFouls = 0; - int _currentQuarter = 1; - - 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; - } - }); - }); - } - setState(() => _isRunning = !_isRunning); - } - - 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(); - } - - @override - Widget build(BuildContext context) { - final screenWidth = MediaQuery.of(context).size.width; - final screenHeight = MediaQuery.of(context).size.height; - - return Scaffold( - body: Stack( - children: [ - // Fundo do Campo - Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/campo.png'), - fit: BoxFit.cover, - alignment: Alignment(0.0, 0.2), - ), - ), - ), - - // Posicionamento Tático (Jogadores como DragTargets) - ..._buildTacticalFormation(screenWidth, screenHeight), - - // Botão Central Play/Pause - Center( - child: GestureDetector( - onTap: _toggleTimer, - child: CircleAvatar( - radius: 35, - backgroundColor: Colors.white, - child: Icon(_isRunning ? Icons.pause : Icons.play_arrow, - color: const Color(0xFF1E2A38), size: 40), - ), - ), - ), - - // Placard Superior - Positioned(top: 0, left: 0, right: 0, child: Center(child: _buildTopScoreboard())), - - // Painel de Ações Inferior (Botões de pontos como Draggables) - Positioned(bottom: 20, left: 0, right: 0, child: _buildActionButtonsPanel()), - - // Botão Fechar - Positioned( - bottom: 20, - right: 20, - child: FloatingActionButton( - backgroundColor: const Color(0xFF1E2A38), - mini: true, - onPressed: () => Navigator.pop(context), - child: const Icon(Icons.close, color: Colors.white), - ), - ), - ], - ), - ); - } - - List _buildTacticalFormation(double w, double h) { - return [ - // CASA (Azul) - Positioned(top: h * 0.25, left: w * 0.02, child: _buildPlayerCard("1", "Russell", false)), - Positioned(top: h * 0.68, left: w * 0.02, child: _buildPlayerCard("15", "Reaves", false)), - Positioned(top: h * 0.48, left: w * 0.20, child: _buildPlayerCard("3", "Davis", false)), - Positioned(top: h * 0.15, left: w * 0.20, child: _buildPlayerCard("6", "James", false)), - Positioned(top: h * 0.80, left: w * 0.20, child: _buildPlayerCard("28", "Hachimura", false)), - - // VISITANTE (Vermelho) - Positioned(top: h * 0.25, right: w * 0.02, child: _buildPlayerCard("7", "Kyle", true)), - Positioned(top: h * 0.68, right: w * 0.02, child: _buildPlayerCard("9", "Serge", true)), - Positioned(top: h * 0.48, right: w * 0.20, child: _buildPlayerCard("2", "Kawhi", true)), - Positioned(top: h * 0.15, right: w * 0.20, child: _buildPlayerCard("14", "Danny", true)), - Positioned(top: h * 0.80, right: w * 0.20, child: _buildPlayerCard("23", "Fred", true)), - ]; - } - - // JOGADOR COMO DRAG TARGET (Recebe os pontos) - Widget _buildPlayerCard(String number, String name, bool isOpponent) { - final teamColor = isOpponent ? const Color(0xFFD92C2C) : const Color(0xFF1E5BB2); - - return DragTarget( - onWillAcceptWithDetails: (details) => true, - onAcceptWithDetails: (details) { - setState(() { - if (isOpponent) { - _opponentScore += details.data; - } else { - _myScore += details.data; - } - }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("$name marcou ${details.data} pontos!"), duration: const Duration(seconds: 1)), - ); - }, - builder: (context, candidateData, rejectedData) { - return AnimatedContainer( - duration: const Duration(milliseconds: 200), - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), - decoration: BoxDecoration( - color: candidateData.isNotEmpty ? Colors.yellow.shade100 : Colors.white, - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: candidateData.isNotEmpty ? Colors.orange : Colors.transparent, - 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: 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: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87)), - const Text("0 Pts | 0 Rbs | 0 Ast", style: TextStyle(fontSize: 12, color: Colors.grey)), - ], - ), - ], - ), - ); - }, - ); - } - - 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, 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, const Color(0xFFD92C2C), true), - ], - ), - ); - } - - Widget _buildTeamSection(String name, int score, int fouls, Color color, bool isOpponent) { - final info = Column( - children: [ - _scoreBox(score, color), - Text("FALTAS: $fouls", style: const TextStyle(color: Colors.yellowAccent, fontSize: 12)), - ], - ); - final teamName = Text(name.toUpperCase(), style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)); - - return Row( - children: isOpponent ? [info, const SizedBox(width: 15), teamName] : [teamName, const SizedBox(width: 15), info], - ); - } - - Widget _timeDisplay() => Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), - decoration: BoxDecoration(color: const Color(0xFF2C3E50), borderRadius: BorderRadius.circular(5)), - 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: 55, height: 45, - alignment: Alignment.center, - decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(8)), - child: Text(score.toString(), style: const TextStyle(color: Colors.white, fontSize: 26, fontWeight: FontWeight.bold)), - ); - - // PAINEL DE AÇÕES (Botões de pontos são DRAGGABLE) - Widget _buildActionButtonsPanel() { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _draggableActionBtn("1", Colors.orange, 1), - _draggableActionBtn("2", Colors.orange, 2), - _draggableActionBtn("3", Colors.orange, 3), - _simpleActionBtn(null, const Color(0xFF1E2A38), Icons.shopping_basket), - ], - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _simpleActionBtn("1", Colors.orange, null, isX: true), - _simpleActionBtn("2", Colors.orange, null, isX: true), - _simpleActionBtn("3", Colors.orange, null, isX: true), - _simpleActionBtn(null, const Color(0xFF1E2A38), Icons.shield), - ], - ), - ], - ); - } - - // BOTÃO ARRASTÁVEL (PONTOS) - Widget _draggableActionBtn(String label, Color color, int points) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: Draggable( - data: points, - feedback: _buildCircleContent(label, color, null, true), // O que voa com o dedo - childWhenDragging: Opacity(opacity: 0.3, child: _buildCircleContent(label, color, null, false)), - child: _buildCircleContent(label, color, null, false), - ), - ); - } - - // BOTÃO SIMPLES (REBOTES/DEFESA) - Widget _simpleActionBtn(String? label, Color color, IconData? icon, {bool isX = false}) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: Stack( - alignment: Alignment.bottomRight, - children: [ - _buildCircleContent(label, color, icon, false), - if (isX) const Icon(Icons.cancel, color: Colors.red, size: 25), - ], - ), - ); - } - - Widget _buildCircleContent(String? label, Color color, IconData? icon, bool isFeedback) { - return Container( - width: isFeedback ? 70 : 60, - height: isFeedback ? 70 : 60, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4)], - ), - alignment: Alignment.center, - child: icon != null - ? Icon(icon, color: Colors.white, size: 35) - : Text(label!, style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, decoration: TextDecoration.none)), - ); - } -}*/ - import 'dart:async'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - class PlacarPage extends StatefulWidget { final String gameId, myTeam, opponentTeam; const PlacarPage({super.key, required this.gameId, required this.myTeam, required this.opponentTeam}); @@ -335,17 +28,49 @@ class _PlacarPageState extends State { 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}, - "Reaves": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, - "Davis": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, - "James": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, - "Hachimura": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, - "Kyle": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, - "Serge": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, - "Kawhi": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, - "Danny": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, - "Fred": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, + "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); @@ -358,47 +83,37 @@ class _PlacarPageState extends State { 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 { - // O TEMPO ESGOTOU (00:00) - _timer?.cancel(); - _isRunning = false; - - // 1. MUDAR O PERÍODO - if (_currentQuarter < 4) { - _currentQuarter++; - - // 2. RESETAR O TEMPO PARA 10 MINUTOS - _duration = const Duration(minutes: 10); - - // 3. RESETAR AS FALTAS DE EQUIPA (O PONTO MAIS IMPORTANTE) - _myFouls = 0; - _opponentFouls = 0; - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Período $_currentQuarter iniciado. Faltas resetadas!'), - backgroundColor: Colors.blue, - ), - ); + 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 { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('FIM DO JOGO!'), backgroundColor: Colors.red), - ); + _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); } - setState(() => _isRunning = !_isRunning); -} + void _useTimeout(bool isOpponent) { setState(() { if (isOpponent) { @@ -421,6 +136,103 @@ void _toggleTimer() { 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( @@ -429,9 +241,7 @@ void _toggleTimer() { children: [ Container( margin: const EdgeInsets.only(left: 60, right: 60, bottom: 50), - decoration: BoxDecoration( - border: Border.all(color: Colors.white, width: 2.0), - ), + decoration: BoxDecoration(border: Border.all(color: Colors.white, width: 2.0)), child: LayoutBuilder( builder: (context, constraints) { final innerWidth = constraints.maxWidth; @@ -439,87 +249,159 @@ void _toggleTimer() { return Stack( children: [ - Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/campo.png'), - fit: BoxFit.cover, - alignment: Alignment(0.0, 0.2) + // 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(), ), ), ), - ..._buildTacticalFormation(innerWidth, innerHeight), + // --- MODO NORMAL DE JOGO --- + if (!_isSelectingShotLocation) ..._buildTacticalFormation(innerWidth, innerHeight), - Positioned( - top: innerHeight * 0.32, - left: 0, - right: 0, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - _dragBtn("F", Colors.orange, "add_foul", icon: Icons.sports), - const SizedBox(width: 40), - _dragBtn("F", Colors.orange, "sub_foul", icon: Icons.block), - ], + 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), ), - const SizedBox(height: 15), - 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())), - Positioned(bottom: 10, left: 0, right: 0, child: _buildActionButtonsPanel()), + + 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 - 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), + // 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), + ), ), - ), - 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( + 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), + ), ), - ), - Positioned( - bottom: 50, left: 10, - child: FloatingActionButton( - heroTag: 'btn_sub_home', backgroundColor: const Color(0xFF1E5BB2), mini: true, - onPressed: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Substituição Casa'))), - child: const Icon(Icons.swap_horiz, 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), + ), + ], + ), ), - ), - Positioned( - bottom: 50, right: 10, - child: FloatingActionButton( - heroTag: 'btn_sub_away', backgroundColor: const Color(0xFFD92C2C), mini: true, - onPressed: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Substituição Visitante'))), - 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), + ), + ], + ), ), - ), ], ), ); @@ -527,94 +409,186 @@ void _toggleTimer() { List _buildTacticalFormation(double w, double h) { return [ - Positioned(top: h * 0.25, left: w * 0.02, child: _buildPlayerCard("1", "Russell", false)), - Positioned(top: h * 0.68, left: w * 0.02, child: _buildPlayerCard("15", "Reaves", false)), - Positioned(top: h * 0.45, left: w * 0.25, child: _buildPlayerCard("3", "Davis", false)), - Positioned(top: h * 0.15, left: w * 0.20, child: _buildPlayerCard("6", "James", false)), - Positioned(top: h * 0.80, left: w * 0.20, child: _buildPlayerCard("28", "Hachimura", false)), + 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("7", "Kyle", true)), - Positioned(top: h * 0.68, right: w * 0.02, child: _buildPlayerCard("9", "Serge", true)), - Positioned(top: h * 0.45, right: w * 0.25, child: _buildPlayerCard("2", "Kawhi", true)), - Positioned(top: h * 0.15, right: w * 0.20, child: _buildPlayerCard("14", "Danny", true)), - Positioned(top: h * 0.80, right: w * 0.20, child: _buildPlayerCard("23", "Fred", true)), + 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(0xFF8B1A1A) : const Color(0xFF1E5BB2); + final prefix = isOpponent ? "sub_opp_" : "sub_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 DragTarget( - onAcceptWithDetails: (details) { - final action = details.data; - 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; - } - 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; - } - 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; } + 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; - // LÓGICA DE FALTAS COM LIMITE DE 5 - else if (action == "add_foul") { - if (isOpponent) { - if (_opponentFouls < 5) _opponentFouls++; - } else { - if (_myFouls < 5) _myFouls++; - } + if (action.startsWith("add_") || action.startsWith("sub_") || action.startsWith("miss_")) { + _handleActionDrag(action, "$prefix$name"); // CHAMA A NOVA LÓGICA DE INTERCEÇÃO + } + else { + setState(() { + if (action.startsWith("sub_my_") && !isOpponent) { + String benchPlayer = action.replaceAll("sub_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("sub_opp_") && isOpponent) { + String benchPlayer = action.replaceAll("sub_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'))); + } + }); } - else if (action == "sub_foul") { - if (isOpponent) { - if (_opponentFouls > 0) _opponentFouls--; - } else { - if (_myFouls > 0) _myFouls--; - } - } - }); - }, - builder: (context, candidateData, rejectedData) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), - decoration: BoxDecoration( - color: candidateData.isNotEmpty ? Colors.orange.shade50 : Colors.white, - borderRadius: BorderRadius.circular(12), - border: Border.all(color: candidateData.isNotEmpty ? Colors.orange : Colors.transparent, width: 2), - boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 6, offset: Offset(0, 3))], + }, + builder: (context, candidateData, rejectedData) { + bool isSubbing = candidateData.any((data) => data != null && data.startsWith("sub_my_") || data != null && data.startsWith("sub_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)), ), - child: Row( + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Container( - width: 40, height: 40, - decoration: BoxDecoration(color: teamColor, borderRadius: BorderRadius.circular(8)), - alignment: Alignment.center, - child: Text(number, style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold)), + Text( + name, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: isFouledOut ? Colors.red : Colors.black87, + decoration: isFouledOut ? TextDecoration.lineThrough : TextDecoration.none + ) ), - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87)), - const SizedBox(height: 2), - Text("${stats["pts"]} Pts | ${stats["rbs"]} Rbs | ${stats["ast"]} Ast", style: const TextStyle(fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w500)), - ], + 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) ), ], ), - ); - }, + ], + ), ); } @@ -688,30 +662,40 @@ void _toggleTimer() { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ + // COLUNA 1: 1 Ponto _columnBtn([ _actionBtn("T.O", const Color(0xFF1E5BB2), () => _useTimeout(false), labelSize: 20), - _dragBtn("1", Colors.orange, "add_pts_1"), - _dragBtn("1", Colors.orange, "sub_pts_1", isX: true), - _dragBtn("AST", Colors.blueGrey, "add_ast"), + _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([ - _dragBtn("2", Colors.orange, "add_pts_2"), - _dragBtn("2", Colors.orange, "sub_pts_2", isX: true), - _dragBtn("STL", Colors.green, "add_stl"), + _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([ - _dragBtn("3", Colors.orange, "add_pts_3"), - _dragBtn("3", Colors.orange, "sub_pts_3", isX: true), - _dragBtn("TOV", Colors.redAccent, "add_tov"), + _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), - _dragBtn("ORB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball), - _dragBtn("DRB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball), - _dragBtn("BLK", Colors.deepPurple, "add_blk"), + _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), ]), ], ); @@ -719,12 +703,32 @@ void _toggleTimer() { Widget _columnBtn(List children) => Column(mainAxisSize: MainAxisSize.min, children: children.map((c) => Padding(padding: const EdgeInsets.only(bottom: 8), child: c)).toList()); - Widget _dragBtn(String label, Color color, String actionData, {IconData? icon, bool isX = false}) { + Widget _dragAndTargetBtn(String label, Color color, String actionData, {IconData? icon, bool isX = false}) { return Draggable( - data: actionData, + data: actionData, feedback: _circle(label, color, icon, true, isX: isX), childWhenDragging: Opacity(opacity: 0.5, child: _circle(label, color, icon, false, isX: isX)), - 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) + ), + ); + } + ), ); } @@ -732,18 +736,138 @@ void _toggleTimer() { 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 _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: BoxDecoration(color: color, shape: BoxShape.circle, boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4)]), + 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: icon != null ? Icon(icon, color: Colors.white, size: 35) : Text(label, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: fontSize, decoration: TextDecoration.none)), + child: content, ), - if (isX) Icon(Icons.cancel, color: Colors.red, size: isFeed ? 30 : 25), + // Í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) + ), + ), ], ); } -} \ No newline at end of file +} diff --git a/pubspec.lock b/pubspec.lock index 720ba58..bf5456b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" clock: dependency: transitive description: @@ -268,18 +268,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.18" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: @@ -553,10 +553,10 @@ packages: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.9" typed_data: dependency: transitive description: