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: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(
|
||||
|
||||
Reference in New Issue
Block a user