pdf e exel
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:playmaker/classe/theme.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart'; // 👇 A MAGIA DO CACHE
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../controllers/team_controller.dart';
|
||||
import '../utils/size_extension.dart';
|
||||
|
||||
@@ -18,9 +19,55 @@ class _StatusPageState extends State<StatusPage> {
|
||||
|
||||
String? _selectedTeamId;
|
||||
String _selectedTeamName = "Selecionar Equipa";
|
||||
String? _selectedTeamLogo;
|
||||
|
||||
String _sortColumn = 'pts';
|
||||
bool _isAscending = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSelectedTeam();
|
||||
}
|
||||
|
||||
Future<void> _loadSelectedTeam() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final savedId = prefs.getString('last_team_id');
|
||||
|
||||
if (savedId != null && mounted) {
|
||||
setState(() {
|
||||
_selectedTeamId = savedId;
|
||||
_selectedTeamName = prefs.getString('last_team_name') ?? "Selecionar Equipa";
|
||||
_selectedTeamLogo = prefs.getString('last_team_logo');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveSelectedTeam() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (_selectedTeamId != null) {
|
||||
await prefs.setString('last_team_id', _selectedTeamId!);
|
||||
await prefs.setString('last_team_name', _selectedTeamName);
|
||||
if (_selectedTeamLogo != null && _selectedTeamLogo!.isNotEmpty) {
|
||||
await prefs.setString('last_team_logo', _selectedTeamLogo!);
|
||||
} else {
|
||||
await prefs.remove('last_team_logo');
|
||||
}
|
||||
}
|
||||
|
||||
final userId = _supabase.auth.currentUser?.id;
|
||||
if (userId != null && _selectedTeamId != null) {
|
||||
try {
|
||||
await _supabase.from('profiles').upsert({
|
||||
'id': userId,
|
||||
'selected_team_id': _selectedTeamId,
|
||||
});
|
||||
} catch (e) {
|
||||
debugPrint("Erro ao guardar equipa no Supabase: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bgColor = Theme.of(context).cardTheme.color ?? Colors.white;
|
||||
@@ -44,7 +91,19 @@ class _StatusPageState extends State<StatusPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(children: [
|
||||
Icon(Icons.shield, color: AppTheme.primaryRed, size: 24 * context.sf),
|
||||
(_selectedTeamLogo != null && _selectedTeamLogo!.isNotEmpty)
|
||||
? ClipOval(
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: _selectedTeamLogo!,
|
||||
width: 24 * context.sf,
|
||||
height: 24 * context.sf,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) => Icon(Icons.shield, color: AppTheme.primaryRed, size: 24 * context.sf),
|
||||
errorWidget: (context, url, error) => Icon(Icons.shield, color: AppTheme.primaryRed, size: 24 * context.sf),
|
||||
),
|
||||
)
|
||||
: Icon(Icons.shield, color: AppTheme.primaryRed, size: 24 * context.sf),
|
||||
|
||||
SizedBox(width: 10 * context.sf),
|
||||
Text(_selectedTeamName, style: TextStyle(fontSize: 16 * context.sf, fontWeight: FontWeight.bold, color: textColor))
|
||||
]),
|
||||
@@ -99,12 +158,11 @@ class _StatusPageState extends State<StatusPage> {
|
||||
);
|
||||
}
|
||||
|
||||
// 👇 AGORA GUARDA TAMBÉM O IMAGE_URL DO MEMBRO PARA MOSTRAR NA TABELA
|
||||
List<Map<String, dynamic>> _aggregateStats(List<dynamic> stats, List<dynamic> games, List<dynamic> members) {
|
||||
Map<String, Map<String, dynamic>> aggregated = {};
|
||||
for (var member in members) {
|
||||
String name = member['name']?.toString() ?? "Desconhecido";
|
||||
String? imageUrl = member['image_url']?.toString(); // 👈 CAPTURA A IMAGEM AQUI
|
||||
String? imageUrl = member['image_url']?.toString();
|
||||
aggregated[name] = {'name': name, 'image_url': imageUrl, 'j': 0, 'pts': 0, 'ast': 0, 'rbs': 0, 'stl': 0, 'blk': 0, 'mvp': 0, 'def': 0};
|
||||
}
|
||||
for (var row in stats) {
|
||||
@@ -140,78 +198,84 @@ class _StatusPageState extends State<StatusPage> {
|
||||
|
||||
Widget _buildStatsGrid(BuildContext context, List<Map<String, dynamic>> players, Map<String, dynamic> teamTotals, Color bgColor, Color textColor) {
|
||||
return Container(
|
||||
color: Colors.transparent,
|
||||
color: Colors.transparent, // 👇 VOLTOU A ESTAR TRANSPARENTE COMO TINHAS ANTES!
|
||||
width: double.infinity,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: DataTable(
|
||||
columnSpacing: 25 * context.sf,
|
||||
headingRowColor: WidgetStateProperty.all(Theme.of(context).colorScheme.surface),
|
||||
dataRowMaxHeight: 60 * context.sf,
|
||||
dataRowMinHeight: 60 * context.sf,
|
||||
columns: [
|
||||
DataColumn(label: Text('JOGADOR', style: TextStyle(color: textColor))),
|
||||
_buildSortableColumn(context, 'J', 'j', textColor),
|
||||
_buildSortableColumn(context, 'PTS', 'pts', textColor),
|
||||
_buildSortableColumn(context, 'AST', 'ast', textColor),
|
||||
_buildSortableColumn(context, 'RBS', 'rbs', textColor),
|
||||
_buildSortableColumn(context, 'STL', 'stl', textColor),
|
||||
_buildSortableColumn(context, 'BLK', 'blk', textColor),
|
||||
_buildSortableColumn(context, 'DEF 🛡️', 'def', textColor),
|
||||
_buildSortableColumn(context, 'MVP 🏆', 'mvp', textColor),
|
||||
],
|
||||
rows: [
|
||||
...players.map((player) => DataRow(cells: [
|
||||
DataCell(
|
||||
Row(
|
||||
children: [
|
||||
// 👇 FOTO DO JOGADOR NA TABELA (COM CACHE!) 👇
|
||||
ClipOval(
|
||||
child: Container(
|
||||
width: 30 * context.sf,
|
||||
height: 30 * context.sf,
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
child: (player['image_url'] != null && player['image_url'].toString().isNotEmpty)
|
||||
? CachedNetworkImage(
|
||||
imageUrl: player['image_url'],
|
||||
fit: BoxFit.cover,
|
||||
fadeInDuration: Duration.zero,
|
||||
placeholder: (context, url) => Icon(Icons.person, size: 18 * context.sf, color: Colors.grey),
|
||||
errorWidget: (context, url, error) => Icon(Icons.person, size: 18 * context.sf, color: Colors.grey),
|
||||
)
|
||||
: Icon(Icons.person, size: 18 * context.sf, color: Colors.grey),
|
||||
physics: const ClampingScrollPhysics(), // Mantém-se o Clamping para não puxar mais do que o ecrã
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: MediaQuery.of(context).size.width),
|
||||
child: DataTable(
|
||||
columnSpacing: 20 * context.sf,
|
||||
horizontalMargin: 16 * context.sf,
|
||||
headingRowColor: WidgetStateProperty.all(Theme.of(context).colorScheme.surface),
|
||||
dataRowMaxHeight: 60 * context.sf,
|
||||
dataRowMinHeight: 60 * context.sf,
|
||||
columns: [
|
||||
DataColumn(label: Text('JOGADOR', style: TextStyle(color: textColor))),
|
||||
_buildSortableColumn(context, 'J', 'j', textColor),
|
||||
_buildSortableColumn(context, 'PTS', 'pts', textColor),
|
||||
_buildSortableColumn(context, 'AST', 'ast', textColor),
|
||||
_buildSortableColumn(context, 'RBS', 'rbs', textColor),
|
||||
_buildSortableColumn(context, 'STL', 'stl', textColor),
|
||||
_buildSortableColumn(context, 'BLK', 'blk', textColor),
|
||||
_buildSortableColumn(context, 'DEF 🛡️', 'def', textColor),
|
||||
_buildSortableColumn(context, 'MVP 🏆', 'mvp', textColor),
|
||||
],
|
||||
rows: [
|
||||
...players.map((player) => DataRow(cells: [
|
||||
DataCell(
|
||||
Row(
|
||||
children: [
|
||||
ClipOval(
|
||||
child: Container(
|
||||
width: 30 * context.sf,
|
||||
height: 30 * context.sf,
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
child: (player['image_url'] != null && player['image_url'].toString().isNotEmpty)
|
||||
? CachedNetworkImage(
|
||||
imageUrl: player['image_url'],
|
||||
fit: BoxFit.cover,
|
||||
fadeInDuration: Duration.zero,
|
||||
placeholder: (context, url) => Icon(Icons.person, size: 18 * context.sf, color: Colors.grey),
|
||||
errorWidget: (context, url, error) => Icon(Icons.person, size: 18 * context.sf, color: Colors.grey),
|
||||
)
|
||||
: Icon(Icons.person, size: 18 * context.sf, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10 * context.sf),
|
||||
Text(player['name'], style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13 * context.sf, color: textColor))
|
||||
]
|
||||
)
|
||||
),
|
||||
DataCell(Center(child: Text(player['j'].toString(), style: TextStyle(color: textColor)))),
|
||||
_buildStatCell(context, player['pts'], textColor, isHighlight: true),
|
||||
_buildStatCell(context, player['ast'], textColor),
|
||||
_buildStatCell(context, player['rbs'], textColor),
|
||||
_buildStatCell(context, player['stl'], textColor),
|
||||
_buildStatCell(context, player['blk'], textColor),
|
||||
_buildStatCell(context, player['def'], textColor, isBlue: true),
|
||||
_buildStatCell(context, player['mvp'], textColor, isGold: true),
|
||||
])),
|
||||
DataRow(
|
||||
color: WidgetStateProperty.all(Theme.of(context).colorScheme.surface.withOpacity(0.5)),
|
||||
cells: [
|
||||
DataCell(Text('TOTAL EQUIPA', style: TextStyle(fontWeight: FontWeight.w900, color: textColor, fontSize: 12 * context.sf))),
|
||||
DataCell(Center(child: Text(teamTotals['j'].toString(), style: TextStyle(fontWeight: FontWeight.bold, color: textColor)))),
|
||||
_buildStatCell(context, teamTotals['pts'], textColor, isHighlight: true),
|
||||
_buildStatCell(context, teamTotals['ast'], textColor),
|
||||
_buildStatCell(context, teamTotals['rbs'], textColor),
|
||||
_buildStatCell(context, teamTotals['stl'], textColor),
|
||||
_buildStatCell(context, teamTotals['blk'], textColor),
|
||||
_buildStatCell(context, teamTotals['def'], textColor, isBlue: true),
|
||||
_buildStatCell(context, teamTotals['mvp'], textColor, isGold: true),
|
||||
]
|
||||
)
|
||||
],
|
||||
SizedBox(width: 10 * context.sf),
|
||||
Text(player['name'], style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13 * context.sf, color: textColor))
|
||||
]
|
||||
)
|
||||
),
|
||||
DataCell(Center(child: Text(player['j'].toString(), style: TextStyle(color: textColor)))),
|
||||
_buildStatCell(context, player['pts'], textColor, isHighlight: true),
|
||||
_buildStatCell(context, player['ast'], textColor),
|
||||
_buildStatCell(context, player['rbs'], textColor),
|
||||
_buildStatCell(context, player['stl'], textColor),
|
||||
_buildStatCell(context, player['blk'], textColor),
|
||||
_buildStatCell(context, player['def'], textColor, isBlue: true),
|
||||
_buildStatCell(context, player['mvp'], textColor, isGold: true),
|
||||
])),
|
||||
DataRow(
|
||||
color: WidgetStateProperty.all(Theme.of(context).colorScheme.surface.withOpacity(0.5)),
|
||||
cells: [
|
||||
DataCell(Text('TOTAL EQUIPA', style: TextStyle(fontWeight: FontWeight.w900, color: textColor, fontSize: 12 * context.sf))),
|
||||
DataCell(Center(child: Text(teamTotals['j'].toString(), style: TextStyle(fontWeight: FontWeight.bold, color: textColor)))),
|
||||
_buildStatCell(context, teamTotals['pts'], textColor, isHighlight: true),
|
||||
_buildStatCell(context, teamTotals['ast'], textColor),
|
||||
_buildStatCell(context, teamTotals['rbs'], textColor),
|
||||
_buildStatCell(context, teamTotals['stl'], textColor),
|
||||
_buildStatCell(context, teamTotals['blk'], textColor),
|
||||
_buildStatCell(context, teamTotals['def'], textColor, isBlue: true),
|
||||
_buildStatCell(context, teamTotals['mvp'], textColor, isGold: true),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -247,10 +311,40 @@ class _StatusPageState extends State<StatusPage> {
|
||||
stream: _teamController.teamsStream,
|
||||
builder: (context, snapshot) {
|
||||
final teams = snapshot.data ?? [];
|
||||
return ListView.builder(itemCount: teams.length, itemBuilder: (context, i) => ListTile(
|
||||
title: Text(teams[i]['name'], style: TextStyle(color: Theme.of(context).colorScheme.onSurface)),
|
||||
onTap: () { setState(() { _selectedTeamId = teams[i]['id']; _selectedTeamName = teams[i]['name']; }); Navigator.pop(context); },
|
||||
));
|
||||
return ListView.builder(itemCount: teams.length, itemBuilder: (context, i) {
|
||||
final team = teams[i];
|
||||
final logoUrl = team['image_url'];
|
||||
|
||||
return ListTile(
|
||||
leading: ClipOval(
|
||||
child: Container(
|
||||
width: 36 * context.sf,
|
||||
height: 36 * context.sf,
|
||||
color: AppTheme.primaryRed.withOpacity(0.1),
|
||||
child: (logoUrl != null && logoUrl.isNotEmpty)
|
||||
? CachedNetworkImage(
|
||||
imageUrl: logoUrl,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) => Icon(Icons.shield, color: AppTheme.primaryRed, size: 20 * context.sf),
|
||||
errorWidget: (context, url, error) => Icon(Icons.shield, color: AppTheme.primaryRed, size: 20 * context.sf),
|
||||
)
|
||||
: Icon(Icons.shield, color: AppTheme.primaryRed, size: 20 * context.sf),
|
||||
),
|
||||
),
|
||||
title: Text(team['name'], style: TextStyle(color: Theme.of(context).colorScheme.onSurface)),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_selectedTeamId = team['id'].toString();
|
||||
_selectedTeamName = team['name'];
|
||||
_selectedTeamLogo = logoUrl;
|
||||
});
|
||||
|
||||
await _saveSelectedTeam();
|
||||
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user