quero que ao voltar para o jogo ele esteja igual quando eu sai

This commit is contained in:
2026-03-04 12:44:27 +00:00
parent af765fc5ab
commit e6b08befc7
8 changed files with 938 additions and 849 deletions

View File

@@ -3,10 +3,12 @@ import 'package:playmaker/pages/PlacarPage.dart'; // Garante que o import está
import '../controllers/team_controller.dart';
import '../controllers/game_controller.dart';
// --- CARD DE EXIBIÇÃO DO JOGO (Mantém-se quase igual) ---
// --- CARD DE EXIBIÇÃO DO JOGO ---
class GameResultCard extends StatelessWidget {
final String gameId;
final String myTeam, opponentTeam, myScore, opponentScore, status, season;
final String? myTeamLogo; // NOVA VARIÁVEL
final String? opponentTeamLogo; // NOVA VARIÁVEL
const GameResultCard({
super.key,
@@ -17,6 +19,8 @@ class GameResultCard extends StatelessWidget {
required this.opponentScore,
required this.status,
required this.season,
this.myTeamLogo, // ADICIONADO AO CONSTRUTOR
this.opponentTeamLogo, // ADICIONADO AO CONSTRUTOR
});
@override
@@ -32,18 +36,29 @@ class GameResultCard extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: _buildTeamInfo(myTeam, const Color(0xFFE74C3C))),
// Passamos a imagem para a função
Expanded(child: _buildTeamInfo(myTeam, const Color(0xFFE74C3C), myTeamLogo)),
_buildScoreCenter(context, gameId),
Expanded(child: _buildTeamInfo(opponentTeam, Colors.black87)),
// Passamos a imagem para a função
Expanded(child: _buildTeamInfo(opponentTeam, Colors.black87, opponentTeamLogo)),
],
),
);
}
Widget _buildTeamInfo(String name, Color color) {
// ATUALIZADO para desenhar a imagem
Widget _buildTeamInfo(String name, Color color, String? logoUrl) {
return Column(
children: [
CircleAvatar(backgroundColor: color, child: const Icon(Icons.shield, color: Colors.white)),
CircleAvatar(
backgroundColor: color,
backgroundImage: (logoUrl != null && logoUrl.isNotEmpty)
? NetworkImage(logoUrl)
: null,
child: (logoUrl == null || logoUrl.isEmpty)
? const Icon(Icons.shield, color: Colors.white)
: null,
),
const SizedBox(height: 4),
Text(name,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
@@ -102,10 +117,10 @@ class GameResultCard extends StatelessWidget {
);
}
// --- POPUP DE CRIAÇÃO (MODIFICADO PARA SUPABASE) ---
// --- POPUP DE CRIAÇÃO ---
class CreateGameDialogManual extends StatefulWidget {
final TeamController teamController;
final GameController gameController; // Recebemos o controller do jogo
final GameController gameController;
const CreateGameDialogManual({
super.key,
@@ -122,7 +137,7 @@ class _CreateGameDialogManualState extends State<CreateGameDialogManual> {
final TextEditingController _myTeamController = TextEditingController();
final TextEditingController _opponentController = TextEditingController();
bool _isLoading = false; // Para mostrar loading no botão
bool _isLoading = false;
@override
void initState() {
@@ -150,7 +165,6 @@ class _CreateGameDialogManualState extends State<CreateGameDialogManual> {
),
const SizedBox(height: 15),
// Usamos Autocomplete para equipas (Assumindo que TeamController já é Supabase)
_buildSearch(label: "Minha Equipa", controller: _myTeamController),
const Padding(padding: EdgeInsets.symmetric(vertical: 8), child: Text("VS", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.grey))),
@@ -171,7 +185,6 @@ class _CreateGameDialogManualState extends State<CreateGameDialogManual> {
if (_myTeamController.text.isNotEmpty && _opponentController.text.isNotEmpty) {
setState(() => _isLoading = true);
// 1. CRIAR NO SUPABASE E OBTER O ID REAL
String? newGameId = await widget.gameController.createGame(
_myTeamController.text,
_opponentController.text,
@@ -181,10 +194,8 @@ class _CreateGameDialogManualState extends State<CreateGameDialogManual> {
setState(() => _isLoading = false);
if (newGameId != null && context.mounted) {
// 2. Fechar Popup
Navigator.pop(context);
// 3. Ir para o Placar com o ID real do banco
Navigator.push(
context,
MaterialPageRoute(
@@ -206,29 +217,70 @@ class _CreateGameDialogManualState extends State<CreateGameDialogManual> {
);
}
// ATUALIZADO para usar Map e mostrar a imagem na lista de pesquisa
Widget _buildSearch({required String label, required TextEditingController controller}) {
return StreamBuilder<List<Map<String, dynamic>>>(
stream: widget.teamController.teamsStream,
builder: (context, snapshot) {
List<String> teamList = snapshot.hasData
? snapshot.data!.map((t) => t['name'].toString()).toList()
: [];
List<Map<String, dynamic>> teamList = snapshot.hasData ? snapshot.data! : [];
return Autocomplete<String>(
optionsBuilder: (val) {
if (val.text.isEmpty) return const Iterable<String>.empty();
return teamList.where((t) => t.toLowerCase().contains(val.text.toLowerCase()));
return Autocomplete<Map<String, dynamic>>(
displayStringForOption: (Map<String, dynamic> option) => option['name'].toString(),
optionsBuilder: (TextEditingValue val) {
if (val.text.isEmpty) return const Iterable<Map<String, dynamic>>.empty();
return teamList.where((t) =>
t['name'].toString().toLowerCase().contains(val.text.toLowerCase()));
},
onSelected: (String selection) {
controller.text = selection;
onSelected: (Map<String, dynamic> selection) {
controller.text = selection['name'].toString();
},
optionsViewBuilder: (context, onSelected, options) {
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4.0,
borderRadius: BorderRadius.circular(8),
child: ConstrainedBox(
// Ajuste do tamanho máximo do pop-up de sugestões
constraints: BoxConstraints(maxHeight: 250, maxWidth: MediaQuery.of(context).size.width * 0.7),
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final option = options.elementAt(index);
final String name = option['name'].toString();
final String? imageUrl = option['image_url'];
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.grey.shade200,
backgroundImage: (imageUrl != null && imageUrl.isNotEmpty)
? NetworkImage(imageUrl)
: null,
child: (imageUrl == null || imageUrl.isEmpty)
? const Icon(Icons.shield, color: Colors.grey)
: null,
),
title: Text(name, style: const TextStyle(fontWeight: FontWeight.bold)),
onTap: () {
onSelected(option);
},
);
},
),
),
),
);
},
fieldViewBuilder: (ctx, txtCtrl, node, submit) {
// Sincronizar o controller interno do Autocomplete com o nosso controller externo
if (txtCtrl.text.isEmpty && controller.text.isNotEmpty) {
txtCtrl.text = controller.text;
}
// Importante: Guardar o valor escrito mesmo que não selecionado da lista
txtCtrl.addListener(() {
controller.text = txtCtrl.text;
});