pagina de jogo
This commit is contained in:
BIN
assets/campo.png
Normal file
BIN
assets/campo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.9 MiB |
@@ -1,18 +1,10 @@
|
|||||||
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';
|
||||||
|
|
||||||
class PlacarPage extends StatefulWidget {
|
class PlacarPage extends StatefulWidget {
|
||||||
final String gameId;
|
final String gameId, myTeam, opponentTeam;
|
||||||
final String myTeam;
|
const PlacarPage({super.key, required this.gameId, required this.myTeam, required this.opponentTeam});
|
||||||
final String opponentTeam;
|
|
||||||
|
|
||||||
const PlacarPage({
|
|
||||||
super.key,
|
|
||||||
required this.gameId,
|
|
||||||
required this.myTeam,
|
|
||||||
required this.opponentTeam,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PlacarPage> createState() => _PlacarPageState();
|
State<PlacarPage> createState() => _PlacarPageState();
|
||||||
@@ -21,6 +13,9 @@ class PlacarPage extends StatefulWidget {
|
|||||||
class _PlacarPageState extends State<PlacarPage> {
|
class _PlacarPageState extends State<PlacarPage> {
|
||||||
int _myScore = 0;
|
int _myScore = 0;
|
||||||
int _opponentScore = 0;
|
int _opponentScore = 0;
|
||||||
|
int _myFouls = 0;
|
||||||
|
int _opponentFouls = 0;
|
||||||
|
int _currentQuarter = 1;
|
||||||
|
|
||||||
Duration _duration = const Duration(minutes: 10);
|
Duration _duration = const Duration(minutes: 10);
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
@@ -29,7 +24,6 @@ class _PlacarPageState extends State<PlacarPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Força a tela na horizontal
|
|
||||||
SystemChrome.setPreferredOrientations([
|
SystemChrome.setPreferredOrientations([
|
||||||
DeviceOrientation.landscapeRight,
|
DeviceOrientation.landscapeRight,
|
||||||
DeviceOrientation.landscapeLeft,
|
DeviceOrientation.landscapeLeft,
|
||||||
@@ -41,95 +35,71 @@ class _PlacarPageState extends State<PlacarPage> {
|
|||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
} else {
|
} else {
|
||||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||||
|
setState(() {
|
||||||
if (_duration.inSeconds > 0) {
|
if (_duration.inSeconds > 0) {
|
||||||
setState(() => _duration -= const Duration(seconds: 1));
|
_duration -= const Duration(seconds: 1);
|
||||||
} else {
|
} else {
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
setState(() => _isRunning = false);
|
_isRunning = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
setState(() => _isRunning = !_isRunning);
|
setState(() => _isRunning = !_isRunning);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _formatTime(Duration d) {
|
String _formatTime(Duration d) =>
|
||||||
return "${d.inMinutes.toString().padLeft(2, '0')}:${d.inSeconds.remainder(60).toString().padLeft(2, '0')}";
|
"${d.inMinutes.toString().padLeft(2, '0')}:${d.inSeconds.remainder(60).toString().padLeft(2, '0')}";
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
// Restaura a vertical ao sair
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
SystemChrome.setPreferredOrientations([
|
|
||||||
DeviceOrientation.portraitUp,
|
|
||||||
DeviceOrientation.portraitDown,
|
|
||||||
]);
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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(
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
// 1. FUNDO (O CAMPO DE BASQUETEBOL)
|
// Fundo do Campo
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Color(0xFFD2B48C), // Cor de madeira temporária
|
image: DecorationImage(
|
||||||
// TODO: Descomenta a linha abaixo e usa a tua imagem do campo
|
image: AssetImage('assets/campo.png'),
|
||||||
// image: DecorationImage(image: AssetImage('assets/court.png'), fit: BoxFit.cover),
|
fit: BoxFit.cover,
|
||||||
|
alignment: Alignment(0.0, 0.2),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
// Linhas do campo desenhadas por cima (Opcional se usares imagem real)
|
|
||||||
child: const CustomPaint(painter: CourtPainterPlaceholder()),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// 2. BARRA SUPERIOR (PLACARD)
|
// Posicionamento Tático (Jogadores como DragTargets)
|
||||||
Positioned(
|
..._buildTacticalFormation(screenWidth, screenHeight),
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: _buildTopScoreboard(),
|
|
||||||
),
|
|
||||||
|
|
||||||
// 3. JOGADORES NO CAMPO (Posições manuais usando Positioned)
|
// Botão Central Play/Pause
|
||||||
// Exemplo de como posicionar os teus jogadores
|
|
||||||
Positioned(top: 120, left: 100, child: _buildPlayerCard("6", "LeBron James")),
|
|
||||||
Positioned(top: 250, left: 160, child: _buildPlayerCard("3", "Anthony Davis")),
|
|
||||||
Positioned(top: 320, left: 280, child: _buildPlayerCard("28", "Rui Hachimura")),
|
|
||||||
Positioned(top: 380, left: 160, child: _buildPlayerCard("1", "D'Angelo Russell")),
|
|
||||||
Positioned(top: 500, left: 100, child: _buildPlayerCard("15", "Austin Reaves")),
|
|
||||||
|
|
||||||
// 4. BOTÃO CENTRAL (PLAY/PAUSE)
|
|
||||||
Center(
|
Center(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: _toggleTimer,
|
onTap: _toggleTimer,
|
||||||
child: Container(
|
child: CircleAvatar(
|
||||||
decoration: BoxDecoration(
|
radius: 35,
|
||||||
shape: BoxShape.circle,
|
backgroundColor: Colors.white,
|
||||||
color: const Color(0xFF2662D9).withOpacity(0.8),
|
child: Icon(_isRunning ? Icons.pause : Icons.play_arrow,
|
||||||
border: Border.all(color: Colors.blueAccent.withOpacity(0.5), width: 8),
|
color: const Color(0xFF1E2A38), size: 40),
|
||||||
),
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Icon(
|
|
||||||
_isRunning ? Icons.pause : Icons.play_arrow,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 40,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// 5. PAINEL DE ACÇÕES EM BAIXO (Estatísticas: 1pt, 2pt, 3pt)
|
// Placard Superior
|
||||||
Positioned(
|
Positioned(top: 0, left: 0, right: 0, child: Center(child: _buildTopScoreboard())),
|
||||||
bottom: 20,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: _buildActionButtonsPanel(),
|
|
||||||
),
|
|
||||||
|
|
||||||
// 6. BOTÃO DE FECHAR / GUARDAR NO CANTO
|
// Painel de Ações Inferior (Botões de pontos como Draggables)
|
||||||
|
Positioned(bottom: 20, left: 0, right: 0, child: _buildActionButtonsPanel()),
|
||||||
|
|
||||||
|
// Botão Fechar
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 20,
|
bottom: 20,
|
||||||
right: 20,
|
right: 20,
|
||||||
@@ -145,119 +115,497 @@ class _PlacarPageState extends State<PlacarPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- WIDGETS AUXILIARES PARA LIMPAR O CÓDIGO ---
|
List<Widget> _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)),
|
||||||
|
|
||||||
Widget _buildTopScoreboard() {
|
// VISITANTE (Vermelho)
|
||||||
return Container(
|
Positioned(top: h * 0.25, right: w * 0.02, child: _buildPlayerCard("7", "Kyle", true)),
|
||||||
color: const Color(0xFF1E2A38), // Azul Escuro da barra
|
Positioned(top: h * 0.68, right: w * 0.02, child: _buildPlayerCard("9", "Serge", true)),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
Positioned(top: h * 0.48, right: w * 0.20, child: _buildPlayerCard("2", "Kawhi", true)),
|
||||||
child: SafeArea(
|
Positioned(top: h * 0.15, right: w * 0.20, child: _buildPlayerCard("14", "Danny", true)),
|
||||||
bottom: false,
|
Positioned(top: h * 0.80, right: w * 0.20, child: _buildPlayerCard("23", "Fred", true)),
|
||||||
child: Row(
|
];
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
// Equipa Casa
|
|
||||||
Text(widget.myTeam.toUpperCase(), style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
_scoreBox(_myScore, const Color(0xFF2662D9)), // Azul
|
|
||||||
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
|
|
||||||
// Relógio
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
|
||||||
decoration: BoxDecoration(color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(8)),
|
|
||||||
child: Text(
|
|
||||||
_formatTime(_duration),
|
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, fontFamily: 'monospace'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
|
|
||||||
// Equipa Visitante
|
|
||||||
_scoreBox(_opponentScore, const Color(0xFFE74C3C)), // Vermelho
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
Text(widget.opponentTeam.toUpperCase(), style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _scoreBox(int score, Color color) {
|
// JOGADOR COMO DRAG TARGET (Recebe os pontos)
|
||||||
return Container(
|
Widget _buildPlayerCard(String number, String name, bool isOpponent) {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
final teamColor = isOpponent ? const Color(0xFFD92C2C) : const Color(0xFF1E5BB2);
|
||||||
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(8)),
|
|
||||||
child: Text(score.toString(), style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildPlayerCard(String number, String name) {
|
return DragTarget<int>(
|
||||||
return Container(
|
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(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF1E2A38).withOpacity(0.9),
|
color: candidateData.isNotEmpty ? Colors.yellow.shade100 : Colors.white,
|
||||||
borderRadius: BorderRadius.circular(8),
|
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))],
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
Container(
|
||||||
backgroundColor: const Color(0xFF2662D9),
|
width: 40, height: 40,
|
||||||
radius: 12,
|
decoration: BoxDecoration(color: teamColor, borderRadius: BorderRadius.circular(8)),
|
||||||
child: Text(number, style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
alignment: Alignment.center,
|
||||||
|
child: Text(number, style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold)),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 12),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(name, style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87)),
|
||||||
const Text("0 Pts | 0 Rbs | 0 Ast", style: TextStyle(color: Colors.grey, fontSize: 8)),
|
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 _buildActionButtonsPanel() {
|
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(
|
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,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// Podes adicionar onTap nestes botões para chamarem setState(() => _myScore += X)
|
_draggableActionBtn("1", Colors.orange, 1),
|
||||||
_actionBtn("1", Colors.orange),
|
_draggableActionBtn("2", Colors.orange, 2),
|
||||||
_actionBtn("2", Colors.orange),
|
_draggableActionBtn("3", Colors.orange, 3),
|
||||||
_actionBtn("3", Colors.orange),
|
_simpleActionBtn(null, const Color(0xFF1E2A38), Icons.shopping_basket),
|
||||||
const SizedBox(width: 20),
|
],
|
||||||
_actionBtn("M", Colors.blueGrey, icon: Icons.sports_basketball),
|
),
|
||||||
_actionBtn("F", Colors.redAccent, icon: Icons.pan_tool),
|
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),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _actionBtn(String label, Color color, {IconData? icon}) {
|
// BOTÃO ARRASTÁVEL (PONTOS)
|
||||||
|
Widget _draggableActionBtn(String label, Color color, int points) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
|
child: Draggable<int>(
|
||||||
|
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(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
width: isFeedback ? 70 : 60,
|
||||||
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
height: isFeedback ? 70 : 60,
|
||||||
padding: const EdgeInsets.all(12),
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4)],
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
child: icon != null
|
child: icon != null
|
||||||
? Icon(icon, color: Colors.white, size: 20)
|
? Icon(icon, color: Colors.white, size: 35)
|
||||||
: Text(label, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16)),
|
: 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});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PlacarPage> createState() => _PlacarPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlacarPageState extends State<PlacarPage> {
|
||||||
|
int _myScore = 0;
|
||||||
|
int _opponentScore = 0;
|
||||||
|
int _myFouls = 0;
|
||||||
|
int _opponentFouls = 0;
|
||||||
|
int _currentQuarter = 1;
|
||||||
|
|
||||||
|
// MAPA PARA GUARDAR ESTATÍSTICAS INDIVIDUAIS (Key: Nome do Jogador)
|
||||||
|
final Map<String, Map<String, int>> _playerStats = {
|
||||||
|
"Russell": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"Reaves": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"Davis": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"James": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"Hachimura": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"Kyle": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"Serge": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"Kawhi": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"Danny": {"pts": 0, "rbs": 0, "ast": 0},
|
||||||
|
"Fred": {"pts": 0, "rbs": 0, "ast": 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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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: [
|
||||||
|
Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(image: AssetImage('assets/campo.png'), fit: BoxFit.cover, alignment: Alignment(0.0, 0.2)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
..._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: 20, left: 0, right: 0, child: _buildActionButtonsPanel()),
|
||||||
|
|
||||||
|
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<Widget> _buildTacticalFormation(double w, double h) {
|
||||||
|
return [
|
||||||
|
// CASA
|
||||||
|
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
|
||||||
|
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)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPlayerCard(String number, String name, bool isOpponent) {
|
||||||
|
final teamColor = isOpponent ? const Color(0xFFD92C2C) : const Color(0xFF1E5BB2);
|
||||||
|
final stats = _playerStats[name]!;
|
||||||
|
|
||||||
|
return DragTarget<int>(
|
||||||
|
onAcceptWithDetails: (details) {
|
||||||
|
setState(() {
|
||||||
|
// Atualiza Placard Geral
|
||||||
|
if (isOpponent) _opponentScore += details.data; else _myScore += details.data;
|
||||||
|
// ATUALIZA ESTATÍSTICA INDIVIDUAL DO JOGADOR
|
||||||
|
stats["pts"] = stats["pts"]! + details.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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))],
|
||||||
|
),
|
||||||
|
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 SizedBox(height: 2),
|
||||||
|
// AGORA MOSTRA OS PONTOS REAIS DO MAPA
|
||||||
|
Text(
|
||||||
|
"${stats["pts"]} Pts | ${stats["rbs"]} Rbs | ${stats["ast"]} Ast",
|
||||||
|
style: const TextStyle(fontSize: 12, color: 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, 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 isOpp) {
|
||||||
|
final info = Column(children: [_scoreBox(score, color), Text("FALTAS: $fouls", style: const TextStyle(color: Colors.yellowAccent, fontSize: 12))]);
|
||||||
|
return Row(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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_dragBtn("1", Colors.orange, 1),
|
||||||
|
_dragBtn("2", Colors.orange, 2),
|
||||||
|
_dragBtn("3", Colors.orange, 3),
|
||||||
|
_actionBtn("ORB", const Color(0xFF1E2A38), () {}, icon: Icons.shopping_basket),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_actionBtn("1", Colors.orange, () {}, isX: true),
|
||||||
|
_actionBtn("2", Colors.orange, () {}, isX: true),
|
||||||
|
_actionBtn("3", Colors.orange, () {}, isX: true),
|
||||||
|
_actionBtn("DRB", const Color(0xFF1E2A38), () {}, icon: Icons.shield),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTÃO ARRASTÁVEL
|
||||||
|
Widget _dragBtn(String label, Color color, int val) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: Draggable<int>(
|
||||||
|
data: val,
|
||||||
|
feedback: _circle(label, color, null, true),
|
||||||
|
childWhenDragging: Opacity(opacity: 0.5, child: _circle(label, color, null, false)),
|
||||||
|
child: _circle(label, color, null, false),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTÃO CLICÁVEL
|
||||||
|
Widget _actionBtn(String label, Color color, VoidCallback onTap, {IconData? icon, bool isX = false}) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
children: [
|
||||||
|
_circle(label, color, icon, false),
|
||||||
|
if (isX) const Icon(Icons.cancel, color: Colors.red, size: 25),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _circle(String label, Color color, IconData? icon, bool isFeed) {
|
||||||
|
return Container(
|
||||||
|
width: isFeed ? 70 : 60, height: isFeed ? 70 : 60,
|
||||||
|
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: icon != null ? Icon(icon, color: Colors.white, size: 35)
|
||||||
|
: Text(label, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 24, decoration: TextDecoration.none)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Classe temporária para desenhar as marcações do campo caso não tenhas imagem
|
|
||||||
class CourtPainterPlaceholder extends CustomPainter {
|
|
||||||
const CourtPainterPlaceholder();
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final paint = Paint()..color = Colors.white.withOpacity(0.5)..style = PaintingStyle.stroke..strokeWidth = 2;
|
|
||||||
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
|
|
||||||
canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2, size.height), paint);
|
|
||||||
canvas.drawCircle(Offset(size.width / 2, size.height / 2), 60, paint);
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
|
||||||
}
|
|
||||||
@@ -57,7 +57,7 @@ flutter:
|
|||||||
|
|
||||||
assets:
|
assets:
|
||||||
- assets/playmaker-logo.png
|
- assets/playmaker-logo.png
|
||||||
|
- assets/campo.png
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/to/resolution-aware-images
|
# https://flutter.dev/to/resolution-aware-images
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user