263 lines
8.9 KiB
Dart
263 lines
8.9 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
class PlacarPage extends StatefulWidget {
|
|
final String gameId;
|
|
final String myTeam;
|
|
final String 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;
|
|
|
|
Duration _duration = const Duration(minutes: 10);
|
|
Timer? _timer;
|
|
bool _isRunning = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
// Força a tela na horizontal
|
|
SystemChrome.setPreferredOrientations([
|
|
DeviceOrientation.landscapeRight,
|
|
DeviceOrientation.landscapeLeft,
|
|
]);
|
|
}
|
|
|
|
void _toggleTimer() {
|
|
if (_isRunning) {
|
|
_timer?.cancel();
|
|
} else {
|
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
if (_duration.inSeconds > 0) {
|
|
setState(() => _duration -= const Duration(seconds: 1));
|
|
} else {
|
|
_timer?.cancel();
|
|
setState(() => _isRunning = false);
|
|
}
|
|
});
|
|
}
|
|
setState(() => _isRunning = !_isRunning);
|
|
}
|
|
|
|
String _formatTime(Duration d) {
|
|
return "${d.inMinutes.toString().padLeft(2, '0')}:${d.inSeconds.remainder(60).toString().padLeft(2, '0')}";
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_timer?.cancel();
|
|
// Restaura a vertical ao sair
|
|
SystemChrome.setPreferredOrientations([
|
|
DeviceOrientation.portraitUp,
|
|
DeviceOrientation.portraitDown,
|
|
]);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: Stack(
|
|
children: [
|
|
// 1. FUNDO (O CAMPO DE BASQUETEBOL)
|
|
Container(
|
|
width: double.infinity,
|
|
height: double.infinity,
|
|
decoration: const BoxDecoration(
|
|
color: Color(0xFFD2B48C), // Cor de madeira temporária
|
|
// TODO: Descomenta a linha abaixo e usa a tua imagem do campo
|
|
// image: DecorationImage(image: AssetImage('assets/court.png'), fit: BoxFit.cover),
|
|
),
|
|
// Linhas do campo desenhadas por cima (Opcional se usares imagem real)
|
|
child: const CustomPaint(painter: CourtPainterPlaceholder()),
|
|
),
|
|
|
|
// 2. BARRA SUPERIOR (PLACARD)
|
|
Positioned(
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
child: _buildTopScoreboard(),
|
|
),
|
|
|
|
// 3. JOGADORES NO CAMPO (Posições manuais usando Positioned)
|
|
// 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(
|
|
child: GestureDetector(
|
|
onTap: _toggleTimer,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: const Color(0xFF2662D9).withOpacity(0.8),
|
|
border: Border.all(color: Colors.blueAccent.withOpacity(0.5), width: 8),
|
|
),
|
|
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)
|
|
Positioned(
|
|
bottom: 20,
|
|
left: 0,
|
|
right: 0,
|
|
child: _buildActionButtonsPanel(),
|
|
),
|
|
|
|
// 6. BOTÃO DE FECHAR / GUARDAR NO CANTO
|
|
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),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// --- WIDGETS AUXILIARES PARA LIMPAR O CÓDIGO ---
|
|
|
|
Widget _buildTopScoreboard() {
|
|
return Container(
|
|
color: const Color(0xFF1E2A38), // Azul Escuro da barra
|
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
|
child: SafeArea(
|
|
bottom: false,
|
|
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) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
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 Container(
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF1E2A38).withOpacity(0.9),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
CircleAvatar(
|
|
backgroundColor: const Color(0xFF2662D9),
|
|
radius: 12,
|
|
child: Text(number, style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(name, style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
|
const Text("0 Pts | 0 Rbs | 0 Ast", style: TextStyle(color: Colors.grey, fontSize: 8)),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildActionButtonsPanel() {
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
// Podes adicionar onTap nestes botões para chamarem setState(() => _myScore += X)
|
|
_actionBtn("1", Colors.orange),
|
|
_actionBtn("2", Colors.orange),
|
|
_actionBtn("3", Colors.orange),
|
|
const SizedBox(width: 20),
|
|
_actionBtn("M", Colors.blueGrey, icon: Icons.sports_basketball),
|
|
_actionBtn("F", Colors.redAccent, icon: Icons.pan_tool),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _actionBtn(String label, Color color, {IconData? icon}) {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 4),
|
|
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
|
padding: const EdgeInsets.all(12),
|
|
child: icon != null
|
|
? Icon(icon, color: Colors.white, size: 20)
|
|
: Text(label, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16)),
|
|
);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
} |