import 'dart:io'; import 'package:flutter/material.dart'; import 'package:playmaker/classe/theme.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:image_picker/image_picker.dart'; import '../utils/size_extension.dart'; import 'login.dart'; // 👇 Necessário para o redirecionamento do logout import '../main.dart'; // 👇 OBRIGATÓRIO PARA LER A VARIÁVEL "themeNotifier" class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @override State createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { // 👇 VARIÁVEIS DE ESTADO PARA FOTO DE PERFIL File? _localImageFile; String? _uploadedImageUrl; bool _isUploadingImage = false; final supabase = Supabase.instance.client; @override void initState() { super.initState(); _loadUserAvatar(); } // 👇 LÊ A IMAGEM ATUAL DA BASE DE DADOS (Tabela 'profiles') void _loadUserAvatar() async { final userId = supabase.auth.currentUser?.id; if (userId == null) return; try { // ⚠️ NOTA: Ajusta 'profiles' e 'avatar_url' se os nomes na tua BD forem diferentes! final data = await supabase .from('profiles') .select('avatar_url') .eq('id', userId) .maybeSingle(); // maybeSingle evita erro se o perfil ainda não existir if (mounted && data != null && data['avatar_url'] != null) { setState(() { _uploadedImageUrl = data['avatar_url']; }); } } catch (e) { print("Erro ao carregar avatar: $e"); } } // ========================================================================= // 👇 A MÁGICA DE ESCOLHER E FAZER UPLOAD DA FOTO 👇 // ========================================================================= Future _handleImageChange() async { final ImagePicker picker = ImagePicker(); // 1. ABRIR GALERIA final XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery); if (pickedFile == null || !mounted) return; try { // 2. MOSTRAR IMAGEM LOCAL E ATIVAR LOADING setState(() { _localImageFile = File(pickedFile.path); _isUploadingImage = true; }); final userId = supabase.auth.currentUser?.id; if (userId == null) throw Exception("Utilizador não autenticado."); final String storagePath = '$userId/profile_picture.png'; // 3. FAZER UPLOAD (Método direto e seguro!) await supabase.storage.from('avatars').upload( storagePath, _localImageFile!, // Envia o ficheiro File diretamente! fileOptions: const FileOptions(cacheControl: '3600', upsert: true) ); // 4. OBTER URL PÚBLICO final String publicUrl = supabase.storage.from('avatars').getPublicUrl(storagePath); // 5. ATUALIZAR NA BASE DE DADOS // ⚠️ NOTA: Garante que a tabela 'profiles' existe e tem o teu user_id await supabase .from('profiles') .upsert({ 'id': userId, // Garante que atualiza o perfil certo ou cria um novo 'avatar_url': publicUrl }); // 6. SUCESSO! if (mounted) { setState(() { _uploadedImageUrl = publicUrl; _isUploadingImage = false; _localImageFile = null; }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Foto atualizada!"), backgroundColor: Colors.green) ); } } catch (e) { if (mounted) { setState(() { _isUploadingImage = false; _localImageFile = null; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Erro: $e"), backgroundColor: AppTheme.primaryRed) ); } } } @override Widget build(BuildContext context) { final Color primaryRed = AppTheme.primaryRed; final Color bgColor = Theme.of(context).scaffoldBackgroundColor; final Color cardColor = Theme.of(context).cardTheme.color ?? Theme.of(context).colorScheme.surface; final Color textColor = Theme.of(context).colorScheme.onSurface; final Color textLightColor = textColor.withOpacity(0.6); bool isDark = Theme.of(context).brightness == Brightness.dark; return Scaffold( backgroundColor: bgColor, appBar: AppBar( backgroundColor: primaryRed, foregroundColor: Colors.white, elevation: 0, centerTitle: true, title: Text( "Perfil e Definições", style: TextStyle(fontSize: 18 * context.sf, fontWeight: FontWeight.w600), ), leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.pop(context), ), ), body: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 16.0 * context.sf, vertical: 24.0 * context.sf), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ========================================== // CARTÃO DE PERFIL // ========================================== Container( padding: EdgeInsets.all(20 * context.sf), decoration: BoxDecoration( color: cardColor, borderRadius: BorderRadius.circular(16 * context.sf), border: Border.all(color: Colors.grey.withOpacity(0.1)), boxShadow: [ BoxShadow(color: Colors.black.withOpacity(0.04), blurRadius: 10, offset: const Offset(0, 4)), ], ), child: Row( children: [ // 👇 IMAGEM TAPPABLE AQUI 👇 _buildTappableProfileAvatar(context, primaryRed), SizedBox(width: 16 * context.sf), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Treinador", style: TextStyle(fontSize: 18 * context.sf, fontWeight: FontWeight.bold, color: textColor), ), SizedBox(height: 4 * context.sf), Text( supabase.auth.currentUser?.email ?? "sem@email.com", style: TextStyle(color: textLightColor, fontSize: 14 * context.sf), ), ], ), ), ], ), ), SizedBox(height: 32 * context.sf), // ========================================== // SECÇÃO: DEFINIÇÕES // ========================================== Padding( padding: EdgeInsets.only(left: 4 * context.sf, bottom: 12 * context.sf), child: Text( "Definições", style: TextStyle(color: textLightColor, fontSize: 14 * context.sf, fontWeight: FontWeight.bold), ), ), Container( decoration: BoxDecoration( color: cardColor, borderRadius: BorderRadius.circular(16 * context.sf), border: Border.all(color: Colors.grey.withOpacity(0.1)), boxShadow: [ BoxShadow(color: Colors.black.withOpacity(0.04), blurRadius: 10, offset: const Offset(0, 4)), ], ), child: ListTile( contentPadding: EdgeInsets.symmetric(horizontal: 20 * context.sf, vertical: 8 * context.sf), leading: Icon(isDark ? Icons.dark_mode : Icons.light_mode, color: primaryRed, size: 28 * context.sf), title: Text( "Modo Escuro", style: TextStyle(fontWeight: FontWeight.bold, color: textColor, fontSize: 16 * context.sf), ), subtitle: Text( "Altera as cores da aplicação", style: TextStyle(color: textLightColor, fontSize: 13 * context.sf), ), trailing: Switch( value: isDark, activeColor: primaryRed, onChanged: (bool value) { themeNotifier.value = value ? ThemeMode.dark : ThemeMode.light; }, ), ), ), SizedBox(height: 32 * context.sf), // ========================================== // SECÇÃO: CONTA // ========================================== Padding( padding: EdgeInsets.only(left: 4 * context.sf, bottom: 12 * context.sf), child: Text( "Conta", style: TextStyle(color: textLightColor, fontSize: 14 * context.sf, fontWeight: FontWeight.bold), ), ), Container( decoration: BoxDecoration( color: cardColor, borderRadius: BorderRadius.circular(16 * context.sf), border: Border.all(color: Colors.grey.withOpacity(0.1)), boxShadow: [ BoxShadow(color: Colors.black.withOpacity(0.04), blurRadius: 10, offset: const Offset(0, 4)), ], ), child: ListTile( contentPadding: EdgeInsets.symmetric(horizontal: 20 * context.sf, vertical: 4 * context.sf), leading: Icon(Icons.logout_outlined, color: primaryRed, size: 26 * context.sf), title: Text( "Terminar Sessão", style: TextStyle(color: primaryRed, fontWeight: FontWeight.bold, fontSize: 15 * context.sf), ), onTap: () => _confirmLogout(context), ), ), SizedBox(height: 50 * context.sf), // ========================================== // VERSÃO DA APP // ========================================== Center( child: Text( "PlayMaker v1.0.0", style: TextStyle(color: textLightColor.withOpacity(0.7), fontSize: 13 * context.sf), ), ), SizedBox(height: 20 * context.sf), ], ), ), ); } // 👇 O WIDGET DA FOTO DE PERFIL (Protegido com GestureDetector) Widget _buildTappableProfileAvatar(BuildContext context, Color primaryRed) { return GestureDetector( onTap: () { print("CLIQUEI NA FOTO! A abrir galeria..."); // 👇 Vê na consola se isto aparece _handleImageChange(); }, child: Stack( alignment: Alignment.center, children: [ CircleAvatar( radius: 36 * context.sf, backgroundColor: primaryRed.withOpacity(0.1), backgroundImage: _isUploadingImage && _localImageFile != null ? FileImage(_localImageFile!) : (_uploadedImageUrl != null && _uploadedImageUrl!.isNotEmpty ? NetworkImage(_uploadedImageUrl!) : null), child: (_uploadedImageUrl == null && !(_isUploadingImage && _localImageFile != null)) ? Icon(Icons.person, color: primaryRed, size: 36 * context.sf) : null, ), // ÍCONE DE LÁPIS Positioned( bottom: 0, right: 0, child: Container( padding: EdgeInsets.all(6 * context.sf), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, shape: BoxShape.circle, border: Border.all(color: Colors.grey.withOpacity(0.2)), ), child: Icon(Icons.edit_outlined, color: primaryRed, size: 16 * context.sf), ), ), // LOADING OVERLAY if (_isUploadingImage) Positioned.fill( child: Container( decoration: BoxDecoration(color: Colors.black.withOpacity(0.4), shape: BoxShape.circle), child: const Padding( padding: EdgeInsets.all(16.0), child: CircularProgressIndicator(color: Colors.white, strokeWidth: 3), ), ), ), ], ), ); } // 👇 FUNÇÃO DE LOGOUT void _confirmLogout(BuildContext context) { showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: Theme.of(context).colorScheme.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16 * context.sf)), title: Text("Terminar Sessão", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold)), content: Text("Tens a certeza que queres sair da conta?", style: TextStyle(color: Theme.of(context).colorScheme.onSurface)), actions: [ TextButton(onPressed: () => Navigator.pop(ctx), child: const Text("Cancelar", style: TextStyle(color: Colors.grey))), ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: AppTheme.primaryRed, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))), onPressed: () async { await Supabase.instance.client.auth.signOut(); if (ctx.mounted) { Navigator.of(ctx).pushAndRemoveUntil( MaterialPageRoute(builder: (context) => const LoginPage()), (Route route) => false, ); } }, child: const Text("Sair", style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)) ), ], ), ); } }