resetar os time outs e mover jogadores

This commit is contained in:
2026-02-27 18:02:55 +00:00
parent 7aa85ac379
commit 6cba3099e0

View File

@@ -313,7 +313,7 @@ class _PlacarPageState extends State<PlacarPage> {
); );
} }
}*/ }*/
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@@ -332,11 +332,9 @@ class _PlacarPageState extends State<PlacarPage> {
int _opponentFouls = 0; int _opponentFouls = 0;
int _currentQuarter = 1; int _currentQuarter = 1;
// VARIÁVEIS DE TIME-OUT (Máximo 3 cada)
int _myTimeoutsUsed = 0; int _myTimeoutsUsed = 0;
int _opponentTimeoutsUsed = 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<String, Map<String, int>> _playerStats = { final Map<String, Map<String, int>> _playerStats = {
"Russell": {"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},
"Reaves": {"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,7 +358,7 @@ class _PlacarPageState extends State<PlacarPage> {
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeRight, DeviceOrientation.landscapeLeft]); SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeRight, DeviceOrientation.landscapeLeft]);
} }
void _toggleTimer() { void _toggleTimer() {
if (_isRunning) { if (_isRunning) {
_timer?.cancel(); _timer?.cancel();
} else { } else {
@@ -369,15 +367,38 @@ class _PlacarPageState extends State<PlacarPage> {
if (_duration.inSeconds > 0) { if (_duration.inSeconds > 0) {
_duration -= const Duration(seconds: 1); _duration -= const Duration(seconds: 1);
} else { } else {
// O TEMPO ESGOTOU (00:00)
_timer?.cancel(); _timer?.cancel();
_isRunning = false; _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); setState(() => _isRunning = !_isRunning);
} }
void _useTimeout(bool isOpponent) { void _useTimeout(bool isOpponent) {
setState(() { setState(() {
if (isOpponent) { if (isOpponent) {
@@ -402,42 +423,101 @@ class _PlacarPageState extends State<PlacarPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final screenHeight = MediaQuery.of(context).size.height;
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFF266174),
body: Stack( 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: [ children: [
Container( Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
image: DecorationImage(image: AssetImage('assets/campo.png'), fit: BoxFit.cover, alignment: Alignment(0.0, 0.2)), image: DecorationImage(
image: AssetImage('assets/campo.png'),
fit: BoxFit.cover,
alignment: Alignment(0.0, 0.2)
),
), ),
), ),
..._buildTacticalFormation(screenWidth, screenHeight), ..._buildTacticalFormation(innerWidth, innerHeight),
Center( Positioned(
child: GestureDetector( 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, onTap: _toggleTimer,
child: CircleAvatar( child: CircleAvatar(
radius: 35, radius: 60,
backgroundColor: Colors.white, backgroundColor: Colors.grey.withOpacity(0.5),
child: Icon(_isRunning ? Icons.pause : Icons.play_arrow, color: const Color(0xFF1E2A38), size: 40), 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(top: 0, left: 0, right: 0, child: Center(child: _buildTopScoreboard())),
Positioned(bottom: 10, left: 0, right: 0, child: _buildActionButtonsPanel()), Positioned(bottom: 10, left: 0, right: 0, child: _buildActionButtonsPanel()),
],
);
},
),
),
// BOTÕES LATERAIS
Positioned( Positioned(
bottom: 20, right: 20, top: 20, left: 10,
child: FloatingActionButton( child: FloatingActionButton(
backgroundColor: const Color(0xFF1E2A38), heroTag: 'btn_save', backgroundColor: const Color(0xFF16202C), mini: true,
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), 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<PlacarPage> {
return [ return [
Positioned(top: h * 0.25, left: w * 0.02, child: _buildPlayerCard("1", "Russell", false)), 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.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.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.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.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.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.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.80, right: w * 0.20, child: _buildPlayerCard("23", "Fred", true)),
]; ];
@@ -468,7 +548,6 @@ class _PlacarPageState extends State<PlacarPage> {
return DragTarget<String>( return DragTarget<String>(
onAcceptWithDetails: (details) { onAcceptWithDetails: (details) {
final action = details.data; final action = details.data;
setState(() { setState(() {
if (action.startsWith("add_pts_")) { if (action.startsWith("add_pts_")) {
int pts = int.parse(action.split("_").last); int pts = int.parse(action.split("_").last);
@@ -477,19 +556,31 @@ class _PlacarPageState extends State<PlacarPage> {
} }
else if (action.startsWith("sub_pts_")) { else if (action.startsWith("sub_pts_")) {
int pts = int.parse(action.split("_").last); int pts = int.parse(action.split("_").last);
if (isOpponent) { if (isOpponent) { _opponentScore = (_opponentScore - pts < 0) ? 0 : _opponentScore - pts; }
_opponentScore = (_opponentScore - pts < 0) ? 0 : _opponentScore - pts; else { _myScore = (_myScore - pts < 0) ? 0 : _myScore - pts; }
} else {
_myScore = (_myScore - pts < 0) ? 0 : _myScore - pts;
}
stats["pts"] = (stats["pts"]! - pts < 0) ? 0 : stats["pts"]! - 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_rbs") { stats["rbs"] = stats["rbs"]! + 1; }
else if (action == "add_ast") { stats["ast"] = stats["ast"]! + 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_stl") { stats["stl"] = stats["stl"]! + 1; }
else if (action == "add_tov") { stats["tov"] = stats["tov"]! + 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_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) { builder: (context, candidateData, rejectedData) {
@@ -517,11 +608,7 @@ class _PlacarPageState extends State<PlacarPage> {
children: [ children: [
Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87)), Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87)),
const SizedBox(height: 2), 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<PlacarPage> {
children: List.generate(3, (index) => Container( children: List.generate(3, (index) => Container(
margin: const EdgeInsets.symmetric(horizontal: 3), margin: const EdgeInsets.symmetric(horizontal: 3),
width: 12, height: 12, width: 12, height: 12,
decoration: BoxDecoration( decoration: BoxDecoration(shape: BoxShape.circle, color: index < timeouts ? Colors.yellow : Colors.grey.shade600, border: Border.all(color: Colors.black26)),
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( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: isOpp 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<PlacarPage> {
child: Text(score.toString(), style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold)), 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() { Widget _buildActionButtonsPanel() {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
// COLUNA 1: T.O Azul, 1, -1, AST _columnBtn([
Column(
mainAxisSize: MainAxisSize.min,
children: [
_actionBtn("T.O", const Color(0xFF1E5BB2), () => _useTimeout(false), labelSize: 20), _actionBtn("T.O", const Color(0xFF1E5BB2), () => _useTimeout(false), labelSize: 20),
const SizedBox(height: 8),
_dragBtn("1", Colors.orange, "add_pts_1"), _dragBtn("1", Colors.orange, "add_pts_1"),
const SizedBox(height: 8),
_dragBtn("1", Colors.orange, "sub_pts_1", isX: true), _dragBtn("1", Colors.orange, "sub_pts_1", isX: true),
const SizedBox(height: 8),
_dragBtn("AST", Colors.blueGrey, "add_ast"), _dragBtn("AST", Colors.blueGrey, "add_ast"),
], ]),
),
const SizedBox(width: 15), const SizedBox(width: 15),
_columnBtn([
// COLUNA 2: 2, -2, STL (Roubos - guardados na memória)
Column(
mainAxisSize: MainAxisSize.min,
children: [
_dragBtn("2", Colors.orange, "add_pts_2"), _dragBtn("2", Colors.orange, "add_pts_2"),
const SizedBox(height: 8),
_dragBtn("2", Colors.orange, "sub_pts_2", isX: true), _dragBtn("2", Colors.orange, "sub_pts_2", isX: true),
const SizedBox(height: 8),
_dragBtn("STL", Colors.green, "add_stl"), _dragBtn("STL", Colors.green, "add_stl"),
], ]),
),
const SizedBox(width: 15), const SizedBox(width: 15),
_columnBtn([
// COLUNA 3: 3, -3, TOV (Perdas - guardadas na memória)
Column(
mainAxisSize: MainAxisSize.min,
children: [
_dragBtn("3", Colors.orange, "add_pts_3"), _dragBtn("3", Colors.orange, "add_pts_3"),
const SizedBox(height: 8),
_dragBtn("3", Colors.orange, "sub_pts_3", isX: true), _dragBtn("3", Colors.orange, "sub_pts_3", isX: true),
const SizedBox(height: 8),
_dragBtn("TOV", Colors.redAccent, "add_tov"), _dragBtn("TOV", Colors.redAccent, "add_tov"),
], ]),
),
const SizedBox(width: 15), const SizedBox(width: 15),
_columnBtn([
// 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), _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), _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), _dragBtn("DRB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball),
const SizedBox(height: 8),
_dragBtn("BLK", Colors.deepPurple, "add_blk"), _dragBtn("BLK", Colors.deepPurple, "add_blk"),
], ]),
),
], ],
); );
} }
// BOTÃO ARRASTÁVEL Widget _columnBtn(List<Widget> 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 _dragBtn(String label, Color color, String actionData, {IconData? icon, bool isX = false}) {
return Padding( return Draggable<String>(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Draggable<String>(
data: actionData, data: actionData,
feedback: _circle(label, color, icon, true, isX: isX), feedback: _circle(label, color, icon, true, isX: isX),
childWhenDragging: Opacity(opacity: 0.5, child: _circle(label, color, icon, false, isX: isX)), childWhenDragging: Opacity(opacity: 0.5, child: _circle(label, color, icon, false, isX: isX)),
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}) { Widget _actionBtn(String label, Color color, VoidCallback onTap, {IconData? icon, bool isX = false, double labelSize = 24}) {
return GestureDetector( return GestureDetector(onTap: onTap, child: _circle(label, color, icon, false, fontSize: labelSize, isX: isX));
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
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}) { Widget _circle(String label, Color color, IconData? icon, bool isFeed, {double fontSize = 20, bool isX = false}) {
return Stack( return Stack(
alignment: Alignment.bottomRight, alignment: Alignment.bottomRight,
@@ -700,9 +740,7 @@ class _PlacarPageState extends State<PlacarPage> {
width: isFeed ? 70 : 60, height: isFeed ? 70 : 60, width: isFeed ? 70 : 60, height: isFeed ? 70 : 60,
decoration: BoxDecoration(color: color, shape: BoxShape.circle, boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4)]), decoration: BoxDecoration(color: color, shape: BoxShape.circle, boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4)]),
alignment: Alignment.center, alignment: Alignment.center,
child: icon != null 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)),
? 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), if (isX) Icon(Icons.cancel, color: Colors.red, size: isFeed ? 30 : 25),
], ],