This commit is contained in:
2026-05-11 17:22:04 +01:00
parent 60656d77e8
commit 1e38c4ad57
9 changed files with 2386 additions and 484 deletions

View File

@@ -1,13 +1,22 @@
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';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:playmaker/classe/theme.dart';
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';
import '../utils/size_extension.dart';
class StatusPage extends StatefulWidget {
const StatusPage({super.key});
final String? initialTeamId;
final String initialTeamName;
final String? initialTeamLogo;
const StatusPage({
super.key,
this.initialTeamId,
this.initialTeamName = "Selecionar Equipa",
this.initialTeamLogo,
});
@override
State<StatusPage> createState() => _StatusPageState();
@@ -16,10 +25,10 @@ class StatusPage extends StatefulWidget {
class _StatusPageState extends State<StatusPage> {
final TeamController _teamController = TeamController();
final _supabase = Supabase.instance.client;
String? _selectedTeamId;
String _selectedTeamName = "Selecionar Equipa";
String? _selectedTeamLogo;
late String? _selectedTeamId;
late String _selectedTeamName;
late String? _selectedTeamLogo;
String _sortColumn = 'pts';
bool _isAscending = false;
@@ -27,34 +36,61 @@ class _StatusPageState extends State<StatusPage> {
@override
void initState() {
super.initState();
_loadSelectedTeam();
_selectedTeamId = widget.initialTeamId;
_selectedTeamName = widget.initialTeamName;
_selectedTeamLogo = widget.initialTeamLogo;
// Se não vieram parâmetros da HomeScreen, tenta carregar do SharedPreferences
if (_selectedTeamId == null) {
_loadSelectedTeamFallback();
}
}
Future<void> _loadSelectedTeam() async {
final prefs = await SharedPreferences.getInstance();
final savedId = prefs.getString('last_team_id');
if (savedId != null && mounted) {
String _prefsKey(String key) {
final userId = _supabase.auth.currentUser?.id ?? 'guest';
return '${key}_$userId';
}
@override
void didUpdateWidget(StatusPage oldWidget) {
super.didUpdateWidget(oldWidget);
// Quando a HomeScreen muda a equipa, a StatusPage atualiza automaticamente
if (widget.initialTeamId != oldWidget.initialTeamId) {
setState(() {
_selectedTeamId = savedId;
_selectedTeamName = prefs.getString('last_team_name') ?? "Selecionar Equipa";
_selectedTeamLogo = prefs.getString('last_team_logo');
_selectedTeamId = widget.initialTeamId;
_selectedTeamName = widget.initialTeamName;
_selectedTeamLogo = widget.initialTeamLogo;
});
}
}
Future<void> _saveSelectedTeam() async {
/// Fallback: só usado se a HomeScreen não passou nenhuma equipa ainda
Future<void> _loadSelectedTeamFallback() async {
final prefs = await SharedPreferences.getInstance();
final savedId = prefs.getString(_prefsKey('last_team_id'));
if (savedId != null && mounted) {
setState(() {
_selectedTeamId = savedId;
_selectedTeamName = prefs.getString(_prefsKey('last_team_name')) ?? "Selecionar Equipa";
_selectedTeamLogo = prefs.getString(_prefsKey('last_team_logo'));
});
}
}
/// Guarda a equipa selecionada localmente (quando muda dentro da StatusPage)
Future<void> _saveSelectedTeamLocally() async {
final prefs = await SharedPreferences.getInstance();
if (_selectedTeamId != null) {
await prefs.setString('last_team_id', _selectedTeamId!);
await prefs.setString('last_team_name', _selectedTeamName);
await prefs.setString(_prefsKey('last_team_id'), _selectedTeamId!);
await prefs.setString(_prefsKey('last_team_name'), _selectedTeamName);
if (_selectedTeamLogo != null && _selectedTeamLogo!.isNotEmpty) {
await prefs.setString('last_team_logo', _selectedTeamLogo!);
await prefs.setString(_prefsKey('last_team_logo'), _selectedTeamLogo!);
} else {
await prefs.remove('last_team_logo');
await prefs.remove(_prefsKey('last_team_logo'));
}
}
// Também guarda no Supabase
final userId = _supabase.auth.currentUser?.id;
if (userId != null && _selectedTeamId != null) {
try {
@@ -82,10 +118,12 @@ class _StatusPageState extends State<StatusPage> {
child: Container(
padding: EdgeInsets.all(12 * context.sf),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(15 * context.sf),
color: bgColor,
borderRadius: BorderRadius.circular(15 * context.sf),
border: Border.all(color: Colors.grey.withOpacity(0.2)),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 5)]
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 5)
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -98,14 +136,23 @@ class _StatusPageState extends State<StatusPage> {
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),
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))
: 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)),
]),
Icon(Icons.arrow_drop_down, color: textColor),
],
@@ -116,106 +163,204 @@ class _StatusPageState extends State<StatusPage> {
Expanded(
child: _selectedTeamId == null
? Center(child: Text("Seleciona uma equipa acima.", style: TextStyle(color: Colors.grey, fontSize: 14 * context.sf)))
: StreamBuilder<List<Map<String, dynamic>>>(
stream: _supabase.from('player_stats_with_names').stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!),
builder: (context, statsSnapshot) {
return StreamBuilder<List<Map<String, dynamic>>>(
stream: _supabase.from('games').stream(primaryKey: ['id']).eq('my_team', _selectedTeamName),
builder: (context, gamesSnapshot) {
return StreamBuilder<List<Map<String, dynamic>>>(
stream: _supabase.from('members').stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!),
builder: (context, membersSnapshot) {
if (statsSnapshot.connectionState == ConnectionState.waiting || gamesSnapshot.connectionState == ConnectionState.waiting || membersSnapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator(color: AppTheme.primaryRed));
}
? Center(
child: Text(
"Seleciona uma equipa acima.",
style: TextStyle(
color: Colors.grey, fontSize: 14 * context.sf),
),
)
: StreamBuilder<List<Map<String, dynamic>>>(
stream: _supabase
.from('player_stats_with_names')
.stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!),
builder: (context, statsSnapshot) {
return StreamBuilder<List<Map<String, dynamic>>>(
stream: _supabase
.from('games')
.stream(primaryKey: ['id']).eq('my_team', _selectedTeamName),
builder: (context, gamesSnapshot) {
return StreamBuilder<List<Map<String, dynamic>>>(
stream: _supabase
.from('members')
.stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!),
builder: (context, membersSnapshot) {
if (statsSnapshot.connectionState ==
ConnectionState.waiting ||
gamesSnapshot.connectionState ==
ConnectionState.waiting ||
membersSnapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(
color: AppTheme.primaryRed));
}
final membersData = membersSnapshot.data ?? [];
if (membersData.isEmpty) return Center(child: Text("Esta equipa não tem jogadores registados.", style: TextStyle(color: Colors.grey, fontSize: 14 * context.sf)));
final membersData = membersSnapshot.data ?? [];
if (membersData.isEmpty) {
return Center(
child: Text(
"Esta equipa não tem jogadores registados.",
style: TextStyle(
color: Colors.grey,
fontSize: 14 * context.sf)));
}
final statsData = statsSnapshot.data ?? [];
final gamesData = gamesSnapshot.data ?? [];
final totalGamesPlayedByTeam = gamesData.where((g) => g['status'] == 'Terminado').length;
final statsData = statsSnapshot.data ?? [];
final gamesData = gamesSnapshot.data ?? [];
final totalGamesPlayedByTeam = gamesData
.where((g) => g['status'] == 'Terminado')
.length;
final List<Map<String, dynamic>> playerTotals = _aggregateStats(statsData, gamesData, membersData);
final teamTotals = _calculateTeamTotals(playerTotals, totalGamesPlayedByTeam);
final List<Map<String, dynamic>> playerTotals =
_aggregateStats(statsData, gamesData, membersData);
final teamTotals = _calculateTeamTotals(
playerTotals, totalGamesPlayedByTeam);
playerTotals.sort((a, b) {
var valA = a[_sortColumn] ?? 0;
var valB = b[_sortColumn] ?? 0;
return _isAscending ? valA.compareTo(valB) : valB.compareTo(valA);
});
playerTotals.sort((a, b) {
var valA = a[_sortColumn] ?? 0;
var valB = b[_sortColumn] ?? 0;
return _isAscending
? valA.compareTo(valB)
: valB.compareTo(valA);
});
return _buildStatsGrid(context, playerTotals, teamTotals, bgColor, textColor);
}
);
}
);
}
),
return _buildStatsGrid(
context, playerTotals, teamTotals, bgColor, textColor);
},
);
},
);
},
),
),
],
);
}
List<Map<String, dynamic>> _aggregateStats(List<dynamic> stats, List<dynamic> games, List<dynamic> members) {
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();
aggregated[name] = {'name': name, 'image_url': imageUrl, 'j': 0, 'pts': 0, 'ast': 0, 'rbs': 0, 'stl': 0, 'blk': 0, 'mvp': 0, 'def': 0};
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) {
String name = row['player_name']?.toString() ?? "Desconhecido";
if (!aggregated.containsKey(name)) aggregated[name] = {'name': name, 'image_url': null, 'j': 0, 'pts': 0, 'ast': 0, 'rbs': 0, 'stl': 0, 'blk': 0, 'mvp': 0, 'def': 0};
aggregated[name]!['j'] += 1;
if (!aggregated.containsKey(name)) {
aggregated[name] = {
'name': name,
'image_url': null,
'j': 0,
'pts': 0,
'ast': 0,
'rbs': 0,
'stl': 0,
'blk': 0,
'mvp': 0,
'def': 0,
};
}
aggregated[name]!['j'] += 1;
aggregated[name]!['pts'] += (row['pts'] ?? 0);
aggregated[name]!['ast'] += (row['ast'] ?? 0);
aggregated[name]!['rbs'] += (row['rbs'] ?? 0);
aggregated[name]!['stl'] += (row['stl'] ?? 0);
aggregated[name]!['blk'] += (row['blk'] ?? 0);
}
for (var game in games) {
String? mvp = game['mvp_name'];
String? defRaw = game['top_def_name'];
if (mvp != null && aggregated.containsKey(mvp)) aggregated[mvp]!['mvp'] += 1;
if (mvp != null && aggregated.containsKey(mvp)) {
aggregated[mvp]!['mvp'] += 1;
}
if (defRaw != null) {
String defName = defRaw.split(' (')[0].trim();
if (aggregated.containsKey(defName)) aggregated[defName]!['def'] += 1;
String defName = defRaw.split(' (')[0].trim();
if (aggregated.containsKey(defName)) {
aggregated[defName]!['def'] += 1;
}
}
}
return aggregated.values.toList();
}
Map<String, dynamic> _calculateTeamTotals(List<Map<String, dynamic>> players, int teamGames) {
int tPts = 0, tAst = 0, tRbs = 0, tStl = 0, tBlk = 0, tMvp = 0, tDef = 0;
Map<String, dynamic> _calculateTeamTotals(
List<Map<String, dynamic>> players, int teamGames) {
int tPts = 0,
tAst = 0,
tRbs = 0,
tStl = 0,
tBlk = 0,
tMvp = 0,
tDef = 0;
for (var p in players) {
tPts += (p['pts'] as int); tAst += (p['ast'] as int); tRbs += (p['rbs'] as int); tStl += (p['stl'] as int); tBlk += (p['blk'] as int); tMvp += (p['mvp'] as int); tDef += (p['def'] as int);
tPts += (p['pts'] as int);
tAst += (p['ast'] as int);
tRbs += (p['rbs'] as int);
tStl += (p['stl'] as int);
tBlk += (p['blk'] as int);
tMvp += (p['mvp'] as int);
tDef += (p['def'] as int);
}
return {'name': 'TOTAL EQUIPA', 'image_url': null, 'j': teamGames, 'pts': tPts, 'ast': tAst, 'rbs': tRbs, 'stl': tStl, 'blk': tBlk, 'mvp': tMvp, 'def': tDef};
return {
'name': 'TOTAL EQUIPA',
'image_url': null,
'j': teamGames,
'pts': tPts,
'ast': tAst,
'rbs': tRbs,
'stl': tStl,
'blk': tBlk,
'mvp': tMvp,
'def': tDef,
};
}
Widget _buildStatsGrid(BuildContext context, List<Map<String, dynamic>> players, Map<String, dynamic> teamTotals, Color bgColor, Color textColor) {
Widget _buildStatsGrid(
BuildContext context,
List<Map<String, dynamic>> players,
Map<String, dynamic> teamTotals,
Color bgColor,
Color textColor) {
return Container(
color: Colors.transparent, // 👇 VOLTOU A ESTAR TRANSPARENTE COMO TINHAS ANTES!
color: Colors.transparent,
width: double.infinity,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
physics: const BouncingScrollPhysics(),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const ClampingScrollPhysics(), // Mantém-se o Clamping para não puxar mais do que o ecrã
physics: const ClampingScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: MediaQuery.of(context).size.width),
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),
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))),
DataColumn(
label: Text('JOGADOR',
style: TextStyle(color: textColor))),
_buildSortableColumn(context, 'J', 'j', textColor),
_buildSortableColumn(context, 'PTS', 'pts', textColor),
_buildSortableColumn(context, 'AST', 'ast', textColor),
@@ -227,53 +372,83 @@ class _StatusPageState extends State<StatusPage> {
],
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),
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),
])),
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)),
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),
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),
]
)
_buildStatCell(context, teamTotals['def'], textColor,
isBlue: true),
_buildStatCell(context, teamTotals['mvp'], textColor,
isGold: true),
],
),
],
),
),
@@ -282,70 +457,123 @@ class _StatusPageState extends State<StatusPage> {
);
}
DataColumn _buildSortableColumn(BuildContext context, String title, String sortKey, Color textColor) {
return DataColumn(label: InkWell(
onTap: () => setState(() {
if (_sortColumn == sortKey) _isAscending = !_isAscending;
else { _sortColumn = sortKey; _isAscending = false; }
}),
child: Row(children: [
Text(title, style: TextStyle(fontSize: 12 * context.sf, fontWeight: FontWeight.bold, color: textColor)),
if (_sortColumn == sortKey) Icon(_isAscending ? Icons.arrow_drop_up : Icons.arrow_drop_down, size: 18 * context.sf, color: AppTheme.primaryRed),
]),
));
DataColumn _buildSortableColumn(
BuildContext context, String title, String sortKey, Color textColor) {
return DataColumn(
label: InkWell(
onTap: () => setState(() {
if (_sortColumn == sortKey) {
_isAscending = !_isAscending;
} else {
_sortColumn = sortKey;
_isAscending = false;
}
}),
child: Row(children: [
Text(title,
style: TextStyle(
fontSize: 12 * context.sf,
fontWeight: FontWeight.bold,
color: textColor)),
if (_sortColumn == sortKey)
Icon(
_isAscending
? Icons.arrow_drop_up
: Icons.arrow_drop_down,
size: 18 * context.sf,
color: AppTheme.primaryRed),
]),
),
);
}
DataCell _buildStatCell(BuildContext context, int value, Color textColor, {bool isHighlight = false, bool isGold = false, bool isBlue = false}) {
return DataCell(Center(child: Container(
padding: EdgeInsets.symmetric(horizontal: 8 * context.sf, vertical: 4 * context.sf),
decoration: BoxDecoration(color: isGold && value > 0 ? Colors.amber.withOpacity(0.2) : (isBlue && value > 0 ? Colors.blue.withOpacity(0.1) : Colors.transparent), borderRadius: BorderRadius.circular(6)),
child: Text(value == 0 ? "-" : value.toString(), style: TextStyle(
fontWeight: (isHighlight || isGold || isBlue) ? FontWeight.w900 : FontWeight.w600,
fontSize: 14 * context.sf, color: isGold && value > 0 ? Colors.orange.shade900 : (isBlue && value > 0 ? Colors.blue.shade800 : (isHighlight ? AppTheme.successGreen : textColor))
)),
)));
DataCell _buildStatCell(BuildContext context, int value, Color textColor,
{bool isHighlight = false, bool isGold = false, bool isBlue = false}) {
return DataCell(Center(
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 8 * context.sf, vertical: 4 * context.sf),
decoration: BoxDecoration(
color: isGold && value > 0
? Colors.amber.withOpacity(0.2)
: (isBlue && value > 0
? Colors.blue.withOpacity(0.1)
: Colors.transparent),
borderRadius: BorderRadius.circular(6),
),
child: Text(
value == 0 ? "-" : value.toString(),
style: TextStyle(
fontWeight: (isHighlight || isGold || isBlue)
? FontWeight.w900
: FontWeight.w600,
fontSize: 14 * context.sf,
color: isGold && value > 0
? Colors.orange.shade900
: (isBlue && value > 0
? Colors.blue.shade800
: (isHighlight ? AppTheme.successGreen : textColor)),
),
),
),
));
}
void _showTeamSelector(BuildContext context) {
showModalBottomSheet(context: context, backgroundColor: Theme.of(context).colorScheme.surface, builder: (context) => StreamBuilder<List<Map<String, dynamic>>>(
stream: _teamController.teamsStream,
builder: (context, snapshot) {
final teams = snapshot.data ?? [];
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);
showModalBottomSheet(
context: context,
backgroundColor: Theme.of(context).colorScheme.surface,
builder: (context) => StreamBuilder<List<Map<String, dynamic>>>(
stream: _teamController.teamsStream,
builder: (context, snapshot) {
final teams = snapshot.data ?? [];
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 _saveSelectedTeamLocally();
if (context.mounted) Navigator.pop(context);
},
);
},
);
});
},
));
},
),
);
}
}