fazer o botao de criqar personagem guardar personagem
This commit is contained in:
121
lib/controllers/stats_controller.dart
Normal file
121
lib/controllers/stats_controller.dart
Normal 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)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
lib/models/person_model.dart
Normal file
17
lib/models/person_model.dart
Normal 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'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
64
lib/screens/team_stats_page.dart
Normal file
64
lib/screens/team_stats_page.dart
Normal 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(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
lib/widgets/stats_widgets.dart
Normal file
90
lib/widgets/stats_widgets.dart
Normal 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)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:playmaker/screens/team_stats_page.dart';
|
||||||
import '../models/team_model.dart';
|
import '../models/team_model.dart';
|
||||||
import '../controllers/team_controllers.dart';
|
import '../controllers/team_controllers.dart';
|
||||||
|
|
||||||
@@ -83,19 +84,23 @@ subtitle: Padding(
|
|||||||
children: [
|
children: [
|
||||||
// Botão Status
|
// Botão Status
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: const BoxConstraints(),
|
constraints: const BoxConstraints(),
|
||||||
icon: const Icon(Icons.bar_chart, color: Colors.blue),
|
icon: const Icon(Icons.bar_chart, color: Colors.blue),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
// NAVEGAÇÃO PARA A NOVA PÁGINA
|
||||||
SnackBar(content: Text("A abrir status de ${team.name}...")),
|
Navigator.push(
|
||||||
);
|
context,
|
||||||
},
|
MaterialPageRoute(
|
||||||
),
|
builder: (context) => TeamStatsPage(team: team),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
// Botão Eliminar
|
// Botão Eliminar
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: const BoxConstraints(),
|
constraints: const BoxConstraints(),
|
||||||
icon: const Icon(Icons.delete_outline, color: Color(0xFFE74C3C)),
|
icon: const Icon(Icons.delete_outline, color: Color(0xFFE74C3C)),
|
||||||
onPressed: () => _confirmDelete(context),
|
onPressed: () => _confirmDelete(context),
|
||||||
|
|||||||
Reference in New Issue
Block a user