fazer o botao de criqar personagem guardar personagem

This commit is contained in:
2026-01-13 17:15:21 +00:00
parent eeb9f0e760
commit fc527084de
5 changed files with 307 additions and 10 deletions

View File

@@ -0,0 +1,121 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import '../models/person_model.dart';
class StatsController {
final FirebaseFirestore _db = FirebaseFirestore.instance;
// --- LÓGICA DE FIREBASE ---
// GRAVAR: Cria o personagem numa sub-coleção dentro da equipa
Future<void> addPerson(String teamId, String name, String type, String number) async {
await _db.collection('teams').doc(teamId).collection('members').add({
'name': name,
'type': type,
'number': number,
'createdAt': FieldValue.serverTimestamp(),
});
}
// LER: Vai buscar todos os membros da equipa em tempo real
Stream<List<Person>> getMembers(String teamId) {
return _db
.collection('teams')
.doc(teamId)
.collection('members')
.orderBy('createdAt', descending: false) // Organiza por ordem de criação
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => Person.fromFirestore(doc.data(), doc.id))
.toList());
}
// --- LÓGICA DE INTERFACE (POPUP) ---
void showAddPersonDialog(BuildContext context, String teamId) {
String selectedType = 'Jogador';
final TextEditingController nameController = TextEditingController();
final TextEditingController numberController = TextEditingController();
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setPopupState) {
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
title: const Text('Novo Personagem'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Seletor: Jogador ou Treinador
DropdownButtonFormField<String>(
value: selectedType,
decoration: const InputDecoration(labelText: 'Tipo'),
items: ['Jogador', 'Treinador']
.map((t) => DropdownMenuItem(value: t, child: Text(t)))
.toList(),
onChanged: (val) {
setPopupState(() {
selectedType = val!;
});
},
),
const SizedBox(height: 15),
// Campo Nome
TextField(
controller: nameController,
textCapitalization: TextCapitalization.words,
decoration: const InputDecoration(
labelText: 'Nome Completo',
hintText: 'Ex: Stephen Curry',
),
),
// Campo Número (Aparece apenas se for Jogador)
if (selectedType == 'Jogador') ...[
const SizedBox(height: 15),
TextField(
controller: numberController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Número da Camisola',
hintText: 'Ex: 30',
),
),
],
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancelar'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00C853),
),
onPressed: () async {
if (nameController.text.isNotEmpty) {
// CHAMA A FUNÇÃO DE GRAVAR DO FIREBASE
await addPerson(
teamId,
nameController.text,
selectedType,
selectedType == 'Jogador' ? numberController.text : '',
);
if (context.mounted) Navigator.pop(context);
}
},
child: const Text('Guardar', style: TextStyle(color: Colors.white)),
),
],
);
},
);
},
);
}
}

View File

@@ -0,0 +1,17 @@
class Person {
final String id;
final String name;
final String type; // 'Jogador' ou 'Treinador'
final String number; // Ex: '30'
Person({required this.id, required this.name, required this.type, required this.number});
factory Person.fromFirestore(Map<String, dynamic> data, String id) {
return Person(
id: id,
name: data['name'] ?? '',
type: data['type'] ?? 'Jogador',
number: data['number'] ?? '',
);
}
}

View File

@@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
import '../models/team_model.dart';
import '../controllers/stats_controller.dart';
import '../widgets/stats_widgets.dart';
class TeamStatsPage extends StatelessWidget {
final Team team;
final StatsController _controller = StatsController(); // Instancia o controller
TeamStatsPage({super.key, required this.team});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F7FA),
body: Column(
children: [
StatsHeader(team: team), // Widget extraído
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SummaryCard(), // Widget extraído
const SizedBox(height: 30),
_buildSectionTitle("Treinadores"),
const SizedBox(height: 30),
_buildSectionTitle("Jogadores"),
const SizedBox(height: 50),
const Center(
child: Text(
"Clica no botão + para adicionar membros",
style: TextStyle(color: Colors.grey, fontStyle: FontStyle.italic),
),
),
],
),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () => _controller.showAddPersonDialog(context, team.id),
backgroundColor: const Color(0xFF00C853),
child: const Icon(Icons.add, color: Colors.white),
),
);
}
Widget _buildSectionTitle(String title) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF2C3E50)),
),
const SizedBox(height: 10),
const Divider(),
],
);
}
}

View File

@@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import '../models/team_model.dart';
class StatsHeader extends StatelessWidget {
final Team team;
const StatsHeader({super.key, required this.team});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 50, left: 16, right: 16, bottom: 25),
decoration: const BoxDecoration(color: Color(0xFF2196F3)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.pop(context),
),
Row(
children: [
_buildLogo(),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(team.name, style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold)),
Text("Temporada ${team.season}", style: const TextStyle(color: Colors.white70, fontSize: 14)),
],
),
],
),
],
),
);
}
Widget _buildLogo() {
return Container(
width: 60, height: 60,
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)),
child: Center(
child: team.imageUrl.startsWith('http')
? ClipRRect(borderRadius: BorderRadius.circular(12), child: Image.network(team.imageUrl))
: Text(team.imageUrl.isEmpty ? "🏀" : team.imageUrl, style: const TextStyle(fontSize: 30)),
),
);
}
}
class SummaryCard extends StatelessWidget {
const SummaryCard({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)],
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_StatItem(label: "Jogos", value: "0", color: Colors.black),
_StatItem(label: "Vitórias", value: "0", color: Colors.green),
_StatItem(label: "Derrotas", value: "0", color: Colors.red),
],
),
);
}
}
class _StatItem extends StatelessWidget {
final String label, value;
final Color color;
const _StatItem({required this.label, required this.value, required this.color});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(value, style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: color)),
Text(label, style: const TextStyle(color: Colors.grey, fontSize: 13)),
],
);
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:playmaker/screens/team_stats_page.dart';
import '../models/team_model.dart';
import '../controllers/team_controllers.dart';
@@ -87,11 +88,15 @@ subtitle: Padding(
constraints: const BoxConstraints(),
icon: const Icon(Icons.bar_chart, color: Colors.blue),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("A abrir status de ${team.name}...")),
// NAVEGAÇÃO PARA A NOVA PÁGINA
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TeamStatsPage(team: team),
),
);
},
),
),
const SizedBox(width: 12),
// Botão Eliminar
IconButton(