fazer a tela de jogo ta tudo no PlacarPage

This commit is contained in:
2026-02-19 10:38:37 +00:00
parent 74453f7cbc
commit 82dd77ae97
5 changed files with 366 additions and 187 deletions

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class PlacarPage extends StatefulWidget {
final String gameId;
@@ -21,11 +22,20 @@ class _PlacarPageState extends State<PlacarPage> {
int _myScore = 0;
int _opponentScore = 0;
// Lógica do Tempo (Exemplo: 10 minutos)
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();
@@ -49,91 +59,205 @@ class _PlacarPageState extends State<PlacarPage> {
@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(
backgroundColor: const Color(0xFF121212),
appBar: AppBar(
title: const Text("Placar Live"),
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
elevation: 0,
),
body: Column(
body: Stack(
children: [
const SizedBox(height: 20),
// Cronómetro
GestureDetector(
onTap: _toggleTimer,
child: Text(
_formatTime(_duration),
style: const TextStyle(color: Colors.white, fontSize: 75, fontWeight: FontWeight.bold, fontFamily: 'monospace'),
),
),
const Text("CLIQUE NO TEMPO PARA INICIAR/PAUSAR", style: TextStyle(color: Colors.grey, fontSize: 10)),
Expanded(
child: Row(
children: [
// Minha Equipa
_buildTeamSide(widget.myTeam, _myScore, (p) => setState(() => _myScore += p), const Color(0xFFE74C3C)),
// Divisor
Container(width: 1, color: Colors.white24, margin: const EdgeInsets.symmetric(vertical: 40)),
// Adversário
_buildTeamSide(widget.opponentTeam, _opponentScore, (p) => setState(() => _opponentScore += p), Colors.blueGrey),
],
// 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()),
),
// Botão Finalizar
Padding(
padding: const EdgeInsets.all(20.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
minimumSize: const Size(double.infinity, 50),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
),
onPressed: () {
// Aqui podes adicionar a lógica para salvar o resultado final no Controller
Navigator.pop(context);
},
child: const Text("FINALIZAR PARTIDA", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
),
// 2. BARRA SUPERIOR (PLACARD)
Positioned(
top: 0,
left: 0,
right: 0,
child: _buildTopScoreboard(),
),
],
),
);
}
Widget _buildTeamSide(String name, int score, Function(int) onAdd, Color color) {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(name, textAlign: TextAlign.center, style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
Text("$score", style: TextStyle(color: color, fontSize: 80, fontWeight: FontWeight.bold)),
const SizedBox(height: 30),
// Botões de Pontuação
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [1, 2, 3].map((p) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: InkWell(
onTap: () => onAdd(p),
child: CircleAvatar(
backgroundColor: color.withOpacity(0.2),
child: Text("+$p", style: TextStyle(color: color, fontWeight: FontWeight.bold)),
// 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,
),
),
)).toList(),
),
),
// 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;
}