diff --git a/lib/pages/PlacarPage.dart b/lib/pages/PlacarPage.dart index c9d84fa..cc35d33 100644 --- a/lib/pages/PlacarPage.dart +++ b/lib/pages/PlacarPage.dart @@ -313,7 +313,7 @@ class _PlacarPageState extends State { ); } }*/ -import 'dart:async'; + import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -332,11 +332,9 @@ class _PlacarPageState extends State { int _opponentFouls = 0; int _currentQuarter = 1; - // VARIÁVEIS DE TIME-OUT (Máximo 3 cada) int _myTimeoutsUsed = 0; int _opponentTimeoutsUsed = 0; - // MAPA PARA GUARDAR ESTATÍSTICAS (Guarda tudo na memória, mas só mostra Pts, Rbs e Ast no campo) 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}, @@ -360,24 +358,47 @@ 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 { - _timer?.cancel(); - _isRunning = false; - } - }); - }); - } - setState(() => _isRunning = !_isRunning); - } +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, + ), + ); + } 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) { @@ -402,42 +423,101 @@ class _PlacarPageState extends State { @override Widget build(BuildContext context) { - final screenWidth = MediaQuery.of(context).size.width; - final screenHeight = MediaQuery.of(context).size.height; - return Scaffold( + backgroundColor: const Color(0xFF266174), body: Stack( children: [ Container( - decoration: const BoxDecoration( - image: DecorationImage(image: AssetImage('assets/campo.png'), fit: BoxFit.cover, alignment: Alignment(0.0, 0.2)), + 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: [ + Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/campo.png'), + fit: BoxFit.cover, + alignment: Alignment(0.0, 0.2) + ), + ), + ), + + ..._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), + ], + ), + 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()), + ], + ); + }, ), ), - ..._buildTacticalFormation(screenWidth, screenHeight), - - 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), - ), - ), - ), - - Positioned(top: 0, left: 0, right: 0, child: Center(child: _buildTopScoreboard())), - - Positioned(bottom: 10, left: 0, right: 0, child: _buildActionButtonsPanel()), - + // BOTÕES LATERAIS Positioned( - bottom: 20, right: 20, + top: 20, left: 10, child: FloatingActionButton( - backgroundColor: const Color(0xFF1E2A38), - mini: true, + 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.close, color: Colors.white), + 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), + ), + ), + 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), ), ), ], @@ -449,13 +529,13 @@ class _PlacarPageState extends State { 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.48, left: w * 0.20, child: _buildPlayerCard("3", "Davis", 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, 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.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)), ]; @@ -468,7 +548,6 @@ class _PlacarPageState extends State { return DragTarget( onAcceptWithDetails: (details) { final action = details.data; - setState(() { if (action.startsWith("add_pts_")) { int pts = int.parse(action.split("_").last); @@ -477,19 +556,31 @@ class _PlacarPageState extends State { } 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; - } + 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; } - // Guarda STL, TOV e BLK na memória, mas não mostra no ecrã do campo 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; } + + // LÓGICA DE FALTAS COM LIMITE DE 5 + else if (action == "add_foul") { + if (isOpponent) { + if (_opponentFouls < 5) _opponentFouls++; + } else { + if (_myFouls < 5) _myFouls++; + } + } + else if (action == "sub_foul") { + if (isOpponent) { + if (_opponentFouls > 0) _opponentFouls--; + } else { + if (_myFouls > 0) _myFouls--; + } + } }); }, builder: (context, candidateData, rejectedData) { @@ -517,11 +608,7 @@ class _PlacarPageState extends State { children: [ Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87)), const SizedBox(height: 2), - // MOSTRA APENAS O MAIS IMPORTANTE NO CAMPO - Text( - "${stats["pts"]} Pts | ${stats["rbs"]} Rbs | ${stats["ast"]} Ast", - style: const TextStyle(fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w500) - ), + Text("${stats["pts"]} Pts | ${stats["rbs"]} Rbs | ${stats["ast"]} Ast", style: const TextStyle(fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w500)), ], ), ], @@ -564,29 +651,22 @@ class _PlacarPageState extends State { 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), - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: index < timeouts ? Colors.yellow : Colors.grey.shade600, border: Border.all(color: Colors.black26)), )), ); - - final info = Column( - children: [ - _scoreBox(score, color), - const SizedBox(height: 4), - Text("FALTAS: $fouls", style: const TextStyle(color: Colors.yellowAccent, fontSize: 12)), - const SizedBox(height: 4), - timeoutIndicators, - ] - ); - return Row( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: isOpp - ? [info, 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), info] + ? [ + 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]) + ] ); } @@ -603,95 +683,55 @@ class _PlacarPageState extends State { child: Text(score.toString(), style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold)), ); - // --- PAINEL EM COLUNAS ORGANIZADAS COM TODOS OS BOTÕES --- Widget _buildActionButtonsPanel() { return Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ - // COLUNA 1: T.O Azul, 1, -1, AST - Column( - mainAxisSize: MainAxisSize.min, - children: [ - _actionBtn("T.O", const Color(0xFF1E5BB2), () => _useTimeout(false), labelSize: 20), - const SizedBox(height: 8), - _dragBtn("1", Colors.orange, "add_pts_1"), - const SizedBox(height: 8), - _dragBtn("1", Colors.orange, "sub_pts_1", isX: true), - const SizedBox(height: 8), - _dragBtn("AST", Colors.blueGrey, "add_ast"), - ], - ), + _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"), + ]), const SizedBox(width: 15), - - // COLUNA 2: 2, -2, STL (Roubos - guardados na memória) - Column( - mainAxisSize: MainAxisSize.min, - children: [ - _dragBtn("2", Colors.orange, "add_pts_2"), - const SizedBox(height: 8), - _dragBtn("2", Colors.orange, "sub_pts_2", isX: true), - const SizedBox(height: 8), - _dragBtn("STL", Colors.green, "add_stl"), - ], - ), + _columnBtn([ + _dragBtn("2", Colors.orange, "add_pts_2"), + _dragBtn("2", Colors.orange, "sub_pts_2", isX: true), + _dragBtn("STL", Colors.green, "add_stl"), + ]), const SizedBox(width: 15), - - // COLUNA 3: 3, -3, TOV (Perdas - guardadas na memória) - Column( - mainAxisSize: MainAxisSize.min, - children: [ - _dragBtn("3", Colors.orange, "add_pts_3"), - const SizedBox(height: 8), - _dragBtn("3", Colors.orange, "sub_pts_3", isX: true), - const SizedBox(height: 8), - _dragBtn("TOV", Colors.redAccent, "add_tov"), - ], - ), + _columnBtn([ + _dragBtn("3", Colors.orange, "add_pts_3"), + _dragBtn("3", Colors.orange, "sub_pts_3", isX: true), + _dragBtn("TOV", Colors.redAccent, "add_tov"), + ]), const SizedBox(width: 15), - - // COLUNA 4: T.O Vermelho, ORB, DRB, BLK (Desarmes - guardados na memória) - Column( - mainAxisSize: MainAxisSize.min, - children: [ - _actionBtn("T.O", const Color(0xFFD92C2C), () => _useTimeout(true), labelSize: 20), - const SizedBox(height: 8), - _dragBtn("ORB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball), - const SizedBox(height: 8), - _dragBtn("DRB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball), - const SizedBox(height: 8), - _dragBtn("BLK", Colors.deepPurple, "add_blk"), - ], - ), + _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"), + ]), ], ); } - // BOTÃO ARRASTÁVEL + 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}) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: 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: _circle(label, color, icon, false, isX: isX), - ), + 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: _circle(label, color, icon, false, isX: isX), ); } - // BOTÃO CLICÁVEL (Usado para o Time-Out) Widget _actionBtn(String label, Color color, VoidCallback onTap, {IconData? icon, bool isX = false, double labelSize = 24}) { - return GestureDetector( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: _circle(label, color, icon, false, fontSize: labelSize, isX: isX), - ), - ); + return GestureDetector(onTap: onTap, child: _circle(label, color, icon, false, fontSize: labelSize, isX: isX)); } - // DESENHO DO CÍRCULO Widget _circle(String label, Color color, IconData? icon, bool isFeed, {double fontSize = 20, bool isX = false}) { return Stack( alignment: Alignment.bottomRight, @@ -700,9 +740,7 @@ class _PlacarPageState extends State { width: isFeed ? 70 : 60, height: isFeed ? 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: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: fontSize, decoration: TextDecoration.none)), + 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)), ), if (isX) Icon(Icons.cancel, color: Colors.red, size: isFeed ? 30 : 25), ],