326 lines
10 KiB
Dart
326 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
import 'dart:io';
|
|
import '../login/login_screen.dart';
|
|
|
|
class PerfilScreen extends StatefulWidget {
|
|
const PerfilScreen({super.key});
|
|
|
|
@override
|
|
State<PerfilScreen> createState() => _PerfilScreenState();
|
|
}
|
|
|
|
class _PerfilScreenState extends State<PerfilScreen> {
|
|
final ImagePicker _imagePicker = ImagePicker();
|
|
String? _avatarUrl;
|
|
String _nome = '';
|
|
String _email = '';
|
|
bool _isLoading = true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadUserData();
|
|
}
|
|
|
|
Future<void> _loadUserData() async {
|
|
try {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user != null) {
|
|
setState(() {
|
|
_email = user.email ?? '';
|
|
});
|
|
|
|
// Fetch user data from users table
|
|
final response = await Supabase.instance.client
|
|
.from('users')
|
|
.select()
|
|
.eq('id', user.id)
|
|
.single();
|
|
|
|
setState(() {
|
|
_nome = response['nome'] ?? '';
|
|
_avatarUrl = response['avatar_url'];
|
|
});
|
|
}
|
|
} catch (e) {
|
|
print('Error loading user data: $e');
|
|
} finally {
|
|
setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
Future<void> _pickImage() async {
|
|
try {
|
|
final XFile? image = await _imagePicker.pickImage(
|
|
source: ImageSource.gallery,
|
|
maxWidth: 512,
|
|
maxHeight: 512,
|
|
imageQuality: 75,
|
|
);
|
|
|
|
if (image != null) {
|
|
await _uploadImage(image);
|
|
}
|
|
} catch (e) {
|
|
_showErrorSnackBar('Erro ao selecionar imagem: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> _uploadImage(XFile image) async {
|
|
final user = Supabase.instance.client.auth.currentUser;
|
|
if (user == null) {
|
|
_showErrorSnackBar('Usuário não autenticado');
|
|
return;
|
|
}
|
|
|
|
setState(() => _isLoading = true);
|
|
|
|
try {
|
|
final file = File(image.path);
|
|
final fileName =
|
|
'${user.id}_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
|
|
|
// Upload to Supabase Storage
|
|
await Supabase.instance.client.storage
|
|
.from('avatars')
|
|
.upload(fileName, file);
|
|
|
|
// Get public URL
|
|
final imageUrl = Supabase.instance.client.storage
|
|
.from('avatars')
|
|
.getPublicUrl(fileName);
|
|
|
|
// Update user's avatar_url in database
|
|
await Supabase.instance.client
|
|
.from('users')
|
|
.update({'avatar_url': imageUrl})
|
|
.eq('id', user.id);
|
|
|
|
setState(() {
|
|
_avatarUrl = imageUrl;
|
|
});
|
|
|
|
_showSuccessSnackBar('Foto atualizada com sucesso!');
|
|
} catch (e) {
|
|
_showErrorSnackBar('Erro ao fazer upload: $e');
|
|
} finally {
|
|
setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
Future<void> _logout() async {
|
|
try {
|
|
await Supabase.instance.client.auth.signOut();
|
|
if (mounted) {
|
|
Navigator.of(context).pushAndRemoveUntil(
|
|
MaterialPageRoute(builder: (_) => const LoginScreen()),
|
|
(route) => false,
|
|
);
|
|
}
|
|
} catch (e) {
|
|
_showErrorSnackBar('Erro ao sair: $e');
|
|
}
|
|
}
|
|
|
|
void _showErrorSnackBar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(message), backgroundColor: Colors.red),
|
|
);
|
|
}
|
|
|
|
void _showSuccessSnackBar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(message), backgroundColor: Colors.green),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFFFE5CC),
|
|
appBar: AppBar(
|
|
backgroundColor: const Color(0xFF0066CC),
|
|
elevation: 0,
|
|
title: const Text(
|
|
'Perfil',
|
|
style: TextStyle(color: Colors.white, fontSize: 20),
|
|
),
|
|
centerTitle: true,
|
|
),
|
|
body: _isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
children: [
|
|
const SizedBox(height: 20),
|
|
|
|
// Avatar section
|
|
GestureDetector(
|
|
onTap: _pickImage,
|
|
child: Stack(
|
|
children: [
|
|
Container(
|
|
width: 120,
|
|
height: 120,
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: const Color(0xFF0066CC),
|
|
width: 3,
|
|
),
|
|
),
|
|
child: _avatarUrl != null
|
|
? ClipOval(
|
|
child: Image.network(
|
|
_avatarUrl!,
|
|
fit: BoxFit.cover,
|
|
errorBuilder: (context, error, stackTrace) {
|
|
return const Icon(
|
|
Icons.person,
|
|
size: 60,
|
|
color: Color(0xFFCCCCCC),
|
|
);
|
|
},
|
|
),
|
|
)
|
|
: const Icon(
|
|
Icons.person,
|
|
size: 60,
|
|
color: Color(0xFFCCCCCC),
|
|
),
|
|
),
|
|
Positioned(
|
|
bottom: 0,
|
|
right: 0,
|
|
child: Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: const BoxDecoration(
|
|
color: Color(0xFF0066CC),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(
|
|
Icons.camera_alt,
|
|
color: Colors.white,
|
|
size: 20,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
// Name section
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: const Color(0xFFE0E0E0)),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'Nome',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Color(0xFF666666),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
_nome.isNotEmpty ? _nome : 'Não definido',
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
color: Color(0xFF333333),
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// Email section
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: const Color(0xFFE0E0E0)),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'Email',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Color(0xFF666666),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
_email,
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
color: Color(0xFF333333),
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
// Logout button
|
|
SizedBox(
|
|
width: double.infinity,
|
|
height: 56,
|
|
child: ElevatedButton(
|
|
onPressed: _logout,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.red,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
elevation: 0,
|
|
),
|
|
child: const Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.logout, color: Colors.white),
|
|
SizedBox(width: 8),
|
|
Text(
|
|
'Sair da Conta',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|