import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../login/login_screen.dart'; import '../theme/app_theme.dart'; class PerfilScreen extends StatefulWidget { const PerfilScreen({super.key}); @override State createState() => _PerfilScreenState(); } class _PerfilScreenState extends State { final ImagePicker _imagePicker = ImagePicker(); String? _avatarUrl; String _nome = ''; String _email = ''; bool _isLoading = true; int _itemCount = 0; int _plansCount = 0; @override void initState() { super.initState(); _loadUserData(); } Future _loadUserData() async { try { final user = Supabase.instance.client.auth.currentUser; if (user == null) return; setState(() => _email = user.email ?? ''); final response = await Supabase.instance.client .from('users') .select() .eq('id', user.id) .maybeSingle(); final items = await Supabase.instance.client .from('items') .select('id') .eq('user_id', user.id); final plans = await Supabase.instance.client .from('plans') .select('id') .eq('user_id', user.id); if (!mounted) return; setState(() { _nome = response?['nome'] ?? ''; _avatarUrl = response?['avatar_url']; _itemCount = (items as List).length; _plansCount = (plans as List).length; }); } catch (e) { debugPrint('Error loading user data: $e'); } finally { if (mounted) setState(() => _isLoading = false); } } Future _pickImage() async { try { final image = await _imagePicker.pickImage( source: ImageSource.gallery, maxWidth: 512, maxHeight: 512, imageQuality: 75, ); if (image != null) await _uploadImage(image); } catch (e) { if (mounted) AppSnack.error(context, 'Erro: $e'); } } Future _uploadImage(XFile image) async { final user = Supabase.instance.client.auth.currentUser; if (user == null) { AppSnack.error(context, 'Utilizador não autenticado'); return; } setState(() => _isLoading = true); try { final file = File(image.path); final fileName = '${user.id}_${DateTime.now().millisecondsSinceEpoch}.jpg'; await Supabase.instance.client.storage .from('avatars') .upload(fileName, file); final imageUrl = Supabase.instance.client.storage .from('avatars') .getPublicUrl(fileName); await Supabase.instance.client .from('users') .update({'avatar_url': imageUrl}) .eq('id', user.id); if (!mounted) return; setState(() => _avatarUrl = imageUrl); AppSnack.success(context, 'Foto atualizada!'); } catch (e) { if (mounted) AppSnack.error(context, 'Erro: $e'); } finally { if (mounted) setState(() => _isLoading = false); } } Future _confirmLogout() async { final ok = await showDialog( context: context, builder: (ctx) => AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.lg), ), title: const Text('Sair da conta?'), content: const Text('Vai ter de fazer login novamente.'), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text('Cancelar'), ), TextButton( onPressed: () => Navigator.pop(ctx, true), style: TextButton.styleFrom(foregroundColor: AppColors.error), child: const Text('Sair'), ), ], ), ); if (ok == true) _logout(); } Future _logout() async { try { await Supabase.instance.client.auth.signOut(); if (mounted) { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => const LoginScreen()), (_) => false, ); } } catch (e) { if (mounted) AppSnack.error(context, 'Erro ao sair: $e'); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, body: SafeArea( bottom: false, child: _isLoading ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( padding: const EdgeInsets.fromLTRB(20, 12, 20, 100), child: Column( children: [ _buildHeader(), const SizedBox(height: 24), _buildAvatar(), const SizedBox(height: 16), Text( _nome.isNotEmpty ? _nome : 'Utilizador', style: AppText.h2, ), const SizedBox(height: 4), Text(_email, style: AppText.bodySecondary), const SizedBox(height: 24), _buildStatsRow(), const SizedBox(height: 24), _buildInfoCard(), const SizedBox(height: 24), AppButton( label: 'Sair da Conta', icon: Icons.logout_rounded, danger: true, onPressed: _confirmLogout, ), ], ), ), ), ); } Widget _buildHeader() { return const Padding( padding: EdgeInsets.symmetric(vertical: 4), child: Align( alignment: Alignment.centerLeft, child: Text('Perfil', style: AppText.h2), ), ); } Widget _buildAvatar() { return Stack( children: [ Container( width: 120, height: 120, decoration: BoxDecoration( gradient: AppColors.brandGradient, shape: BoxShape.circle, boxShadow: AppShadows.brand, ), padding: const EdgeInsets.all(4), child: ClipOval( child: _avatarUrl != null ? Image.network( _avatarUrl!, fit: BoxFit.cover, errorBuilder: (_, __, ___) => Container( color: AppColors.surface, child: const Icon( Icons.person_rounded, size: 60, color: AppColors.textTertiary, ), ), ) : Container( color: AppColors.surface, child: const Icon( Icons.person_rounded, size: 60, color: AppColors.textTertiary, ), ), ), ), Positioned( bottom: 0, right: 0, child: Material( color: AppColors.surface, shape: const CircleBorder(), elevation: 2, child: InkWell( customBorder: const CircleBorder(), onTap: _pickImage, child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( gradient: AppColors.brandGradient, shape: BoxShape.circle, border: Border.all(color: AppColors.surface, width: 3), ), child: const Icon( Icons.camera_alt_rounded, color: Colors.white, size: 16, ), ), ), ), ), ], ); } Widget _buildStatsRow() { return Row( children: [ _statCard( icon: Icons.inventory_2_rounded, color: AppColors.primary, value: '$_itemCount', label: 'Itens', ), const SizedBox(width: 12), _statCard( icon: Icons.calendar_month_rounded, color: AppColors.accent, value: '$_plansCount', label: 'Dias planeados', ), ], ); } Widget _statCard({ required IconData icon, required Color color, required String value, required String label, }) { return Expanded( child: Container( padding: const EdgeInsets.all(16), decoration: AppDecorations.card(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(AppRadius.sm), ), child: Icon(icon, color: color, size: 20), ), const SizedBox(height: 10), Text( value, style: const TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: AppColors.textPrimary, ), ), Text(label, style: AppText.caption), ], ), ), ); } Widget _buildInfoCard() { return Container( decoration: AppDecorations.card(), clipBehavior: Clip.antiAlias, child: Column( children: [ _infoRow( icon: Icons.person_outline_rounded, label: 'Nome', value: _nome.isNotEmpty ? _nome : 'Não definido', ), const Divider(height: 1, thickness: 1, color: AppColors.divider), _infoRow( icon: Icons.alternate_email_rounded, label: 'Email', value: _email, ), ], ), ); } Widget _infoRow({ required IconData icon, required String label, required String value, }) { return Padding( padding: const EdgeInsets.all(16), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: AppColors.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.md), ), child: Icon(icon, color: AppColors.primary, size: 20), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: AppText.caption), const SizedBox(height: 2), Text( value, style: const TextStyle( fontWeight: FontWeight.w600, color: AppColors.textPrimary, fontSize: 15, ), ), ], ), ), ], ), ); } }