import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'dart:io'; import 'package:intl/intl.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/controllers/add_name_email_city.dart'; import 'package:prosappco/src/presentation/widgets/profile/birth_date_picker.dart'; import 'package:prosappco/src/presentation/screens/city.dart'; import 'package:prosappco/src/presentation/screens/new_number.dart'; import 'package:prosappco/src/presentation/screens/new_password.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_checkbox.dart'; import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; import 'package:prosappco/src/providers/user_provider.dart'; import 'package:prosappco/src/services/select_image_profile.dart'; import 'package:prosappco/src/presentation/widgets/profile/gender_dropdown.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; import 'package:provider/provider.dart'; import '../../../components/photo_view.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({Key? key}) : super(key: key); @override State createState() => _ProfileScreenState(); } class _ProfileScreenState extends State { File? imagen_to_upload; final DateFormat formatter = DateFormat('dd/MM/yyyy'); final uid = AuthenticationRepository.instance.getCurrentUserUid(); bool _obscureText = true; final _formKey = GlobalKey(); final controller = Get.put(NameEmailCityController()); final _phoneNumberController = TextEditingController(); final _nameController = TextEditingController(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); late final FirebaseAuth _auth; final FirebaseStorage storage = FirebaseStorage.instance; var photoTemp = ''; var _ciudad = '...'; var _photo = '...'; String? _email = ''; String gender = ''; String genderDb = ''; DateTime? birthDate; String birthDateDb = ''; @override void initState() { super.initState(); _auth = FirebaseAuth.instance; final currentUser = _auth.currentUser; if (currentUser != null && currentUser.phoneNumber != null) { _phoneNumberController.text = currentUser.phoneNumber!; } if (currentUser != null && currentUser.displayName != null) { _nameController.text = currentUser.displayName!; } if (currentUser != null && currentUser.email != null) { _emailController.text = currentUser.email!; } if (gender.isEmpty) { AuthenticationRepository.instance.getGender(uid.toString()).then( (String s) => setState(() { genderDb = s; }), ); } if (birthDate == null) { AuthenticationRepository.instance.getBirthday(uid.toString()).then( (String s) => setState(() { if (s.isNotEmpty) { birthDateDb = s; } }), ); } _email = currentUser?.email; if (_ciudad == '...') { AuthenticationRepository.instance.getCity(uid.toString()).then( (String s) => setState(() { _ciudad = s; }), ); } if (_photo == '...') { AuthenticationRepository.instance.getPhoto(uid.toString()).then( (String s) => setState(() { _photo = s; }), ); } } Future updateImage(image) async { try { await FirebaseFirestore.instance .collection('users') .doc(uid) .update({'photo': image}); } catch (e) { try { await FirebaseFirestore.instance .collection('users') .doc(uid) .set({'photo': image}); } catch (e) { print('Error al agregar la imagen de perfil: $e'); } print('Error al actualizar la imagen de perfil: $e'); } } Future uploadImage(File image) async { final now = DateTime.now(); final formattedDate = DateFormat('HHmmssddMMyyyy').format(now); final milliseconds = (now.microsecondsSinceEpoch / 1000).round(); final random = '$formattedDate$milliseconds'; Reference ref = storage.ref().child('users').child(uid!).child('profile').child(random); final UploadTask uploadTask = ref.putFile(image); final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true); photoTemp = ref.fullPath; if (snapshot.state == TaskState.success) { return true; } else { return false; } } Future downloadImage(Reference ref) async { try { if (_photo == '...' || _photo.isEmpty) { return GestureDetector( onTap: () { _showChoiceDialog(context); }, child: Container( margin: const EdgeInsets.symmetric(vertical: 50), width: 100, height: 100, decoration: BoxDecoration( color: const Color(0xFF2BA4EC), borderRadius: BorderRadius.circular(50), ), child: const Icon( Icons.person, color: Colors.white, size: 90, ), ), ); } else { final imageData = await ref.getData(); if (imageData != null) { final widgetImage = GestureDetector( onTap: () { _showChoiceDialog(context); }, child: Container( margin: const EdgeInsets.symmetric(vertical: 50), child: ClipOval( child: Image.memory( imageData, width: 60, height: 60, fit: BoxFit.cover, ), ), ), ); return widgetImage; } else { return GestureDetector( onTap: () { _showChoiceDialog(context); }, child: Container( margin: const EdgeInsets.symmetric(vertical: 50), width: 100, height: 100, decoration: BoxDecoration( color: const Color(0xFF2BA4EC), borderRadius: BorderRadius.circular(50), ), child: const Icon( Icons.person, color: Colors.white, size: 90, ), ), ); } } } catch (e) { return GestureDetector( onTap: () { _showChoiceDialog(context); }, child: Container( margin: const EdgeInsets.symmetric(vertical: 50), width: 100, height: 100, decoration: BoxDecoration( color: const Color(0xFF2BA4EC), borderRadius: BorderRadius.circular(50), ), child: const Icon( Icons.person, color: Colors.white, size: 90, ), ), ); } } Future updateInfo() async { final currentUser = _auth.currentUser; final currentPhoneNumber = _auth.currentUser!.phoneNumber; String newName = _nameController.text.trim(); String newEmail = _emailController.text.trim(); String newPassword = _passwordController.text.trim(); if (gender.isNotEmpty) { try { await FirebaseFirestore.instance.collection('users').doc(uid).set({ 'gender': gender, }, SetOptions(merge: true)); genderDb = gender; } catch (e) { print('Error al actualizar el genero: $e'); } } if (birthDate != null) { try { await FirebaseFirestore.instance.collection('users').doc(uid).set({ 'birth_date': birthDate.toString(), }, SetOptions(merge: true)); birthDateDb = birthDate.toString(); } catch (e) { print('Error al actualizar la fecha de nacimiento: $e'); } } setState(() {}); if (newName.isEmpty) { Get.snackbar( 'Nombre Invalido', 'Ingresa un nombre válido.', snackPosition: SnackPosition.BOTTOM, ); return; } if (enableLoginWithEmail) { if (newEmail.isEmpty) { Get.snackbar( 'Correo Invalido', 'Ingresa un email válido.', snackPosition: SnackPosition.BOTTOM, ); return; } } if (currentUser?.displayName != newName) { try { await FirebaseAuth.instance.currentUser!.updateDisplayName(newName); await FirebaseFirestore.instance.collection('users').doc(uid).update({ 'name': newName, 'lowerName': newName.toLowerCase(), }); } catch (e) { print('Error al actualizar el nombre: $e'); } } if (enableLoginWithEmail) { if (currentUser?.email != newEmail) { if (newPassword.isNotEmpty) { bool updateEmailSuccess = await updateEmailAndPassword(newEmail, newPassword); if (updateEmailSuccess) { await FirebaseFirestore.instance .collection('users') .doc(uid) .update({ 'email': newEmail, }); } else { return; } } else { Get.snackbar( 'Contraseña Invalida', 'Por favor ingresa una contraseña.', snackPosition: SnackPosition.BOTTOM, ); } } } try { await FirebaseFirestore.instance .collection('users') .doc(uid) .update({'phoneNumber': currentPhoneNumber}); } catch (e) { print('e'); } try { if (imagen_to_upload == null) { } else { final uploaded = await uploadImage(imagen_to_upload!); updateImage(photoTemp); //image } } catch (e) { print('Error al actualizar la imagen de perfil $e'); } WarningSnackbar.show( title: 'Informacion actualizada', message: 'Tu informacion ha sido actualizada con exito.', icon: const Icon( Icons.check, color: Colors.white, ), backgroundColor: Colors.green, ); } Future _updateEmailAndPassword( String newEmail, String currentPassword) async { final user = _auth.currentUser; if (user!.email! == newEmail) { Get.snackbar( 'No se puede actualizar', 'El correo actual no puede ser actualizado.', snackPosition: SnackPosition.BOTTOM, ); return; } if (!newEmail.contains('@') || !newEmail.contains('.')) { Get.snackbar( 'No se puede actualizar', 'Ingresa un correo electrónico valido.', snackPosition: SnackPosition.BOTTOM, ); return; } final emailExistsQuery = await FirebaseFirestore.instance .collection('users') .where('email', isEqualTo: newEmail) .get(); if (emailExistsQuery.docs.isNotEmpty) { Get.snackbar( 'No se puede actualizar', 'El nuevo correo electrónico ya está en uso.', snackPosition: SnackPosition.BOTTOM, ); return; } try { final credential = EmailAuthProvider.credential( email: user.email!, password: currentPassword); await user.reauthenticateWithCredential(credential); await user.updateEmail(newEmail); await FirebaseFirestore.instance .collection('users') .doc(uid) .update({'email': newEmail}); setState(() { _email = newEmail; }); WarningSnackbar.show( title: 'Actualizado exitosamente', message: 'Correo electronico actualizado correctamente.', icon: const Icon(Icons.check, color: Colors.white), backgroundColor: Colors.green, ); if (Navigator.canPop(context)) { Navigator.of(context).pop(); } } catch (e) { WarningSnackbar.show( title: 'No se pudo actualizar el correo', message: 'Verifica tu contraseña actual y asegúrate de que el nuevo correo electrónico no se haya utilizado previamente.', ); print('Error al actualizar el correo electrónico: $e'); } } Future _showEmailUpdateDialog(BuildContext context) async { TextEditingController emailController = TextEditingController(); TextEditingController passwordController = TextEditingController(); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Actualizar Email'), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: emailController, decoration: const InputDecoration(labelText: 'Nuevo Email'), ), TextField( controller: passwordController, decoration: const InputDecoration(labelText: 'Contraseña Actual'), obscureText: true, ), ], ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, child: const Text( 'Cancelar', style: TextStyle(color: Colors.grey), ), ), TextButton( onPressed: () { String newEmail = emailController.text.trim(); String currentPassword = passwordController.text.trim(); if (newEmail.isNotEmpty && currentPassword.isNotEmpty) { _updateEmailAndPassword(newEmail, currentPassword); } }, child: const Text( 'Guardar', style: TextStyle(color: Colors.blue, fontWeight: FontWeight.w600), ), ), ], ); }, ); } Future updateEmailAndPassword(String email, String password) async { final User? user = FirebaseAuth.instance.currentUser; if (user != null) { try { await user.updateEmail(email); await user.updatePassword(password); return true; } catch (e) { WarningSnackbar.show( title: 'Inicia sesión de nuevo', message: 'Para agregar un correo debes haber iniciado sesión recientemente.'); } } return false; } bool enableLoginWithEmail = false; Future _showChoiceDialog(BuildContext context) async { return showDialog( context: context, builder: (BuildContext context) { return AlertDialog( content: SingleChildScrollView( child: ListBody( children: [ GestureDetector( child: const Text( textAlign: TextAlign.center, "Tomar foto", style: TextStyle(color: Color(0xFF2BA4EC)), ), onTap: () async { final imagen = await getImage(1); setState(() { imagen_to_upload = File(imagen[0]!.path); }); Navigator.of(context).pop(); }, ), const Divider(color: Colors.black54), GestureDetector( child: const Text( textAlign: TextAlign.center, "Abrir Galería", style: TextStyle(color: Color(0xFF2BA4EC)), ), onTap: () async { final imagen = await getImage(2); setState(() { imagen_to_upload = File(imagen[0]!.path); }); Navigator.of(context).pop(); }, ), ], ), ), ); }, ); } @override Widget build(BuildContext context) { String city = _ciudad.toString(); final userProvider = Provider.of(context); return Scaffold( appBar: PopAppbar( onPressed: () { Navigator.pop(context); }, label: 'Perfil'), body: SingleChildScrollView( reverse: true, child: Center( child: Column( children: [ GestureDetector( onTap: () { _showChoiceDialog(context); }, child: Container( margin: const EdgeInsets.symmetric(vertical: 20), child: (imagen_to_upload != null) ? LocalPhoto(file: imagen_to_upload!) : ReferencePhoto(ref: storage.ref().child(_photo))), ), Container( width: 300, padding: const EdgeInsets.only(top: 0), child: Form( key: _formKey, child: Column( children: [ TextFormField( controller: _nameController, maxLength: 50, validator: (value) { if (value == null || value.isEmpty) { return 'Porfavor ingrese un nombre.'; } if (value.length < 5) { return 'Debe tener al menos 5 caracteres.'; } return null; }, decoration: const InputDecoration( prefixIcon: Icon(Icons.person_outline), hintText: 'Nombre (Obligatorio)'), ), const SizedBox(), TextFormField( readOnly: true, onTap: () async { final String? ciudad = await Navigator.push( context, CupertinoPageRoute( builder: (BuildContext context) { return const CityScreen(); }, ), ) as String?; if (ciudad != null) { setState(() { _ciudad = ciudad; }); } }, decoration: InputDecoration( prefixIcon: const Icon(Icons.near_me), suffixIcon: const Icon(Icons.arrow_drop_down), hintStyle: city == '' ? const TextStyle() : const TextStyle(color: Colors.black87), hintText: city == '' ? 'Ciudad' : city, ), ), const SizedBox(height: 20.0), TextFormField( controller: _phoneNumberController, readOnly: true, onTap: () { Navigator.of(context).push( CupertinoPageRoute( builder: (BuildContext context) { return const NewNumberScreen(); }, ), ); }, decoration: const InputDecoration( prefixIcon: Icon(Icons.phone_android), suffixIcon: Icon(Icons.edit_outlined), hintText: '+57', ), ), const SizedBox(height: 20.0), genderDb == '' ? GenderDropdown( onChanged: (selectedGender) { setState(() { gender = selectedGender; }); }, ) : const SizedBox(), genderDb == '' ? const SizedBox(height: 20.0) : const SizedBox(), birthDateDb == '' ? BirthDatePicker( onDateSelected: (birthDay) { setState(() { birthDate = birthDay; }); }, controller: TextEditingController( text: birthDate == null ? '' : formatter.format(birthDate!), ), ) : const SizedBox(), _email != null ? TextFormField( onTap: () { _showEmailUpdateDialog(context); }, readOnly: true, controller: _emailController, decoration: const InputDecoration( prefixIcon: Icon(Icons.email_outlined), hintText: 'Email (Obligatorio)'), ) : const SizedBox(), const SizedBox(height: 20), _email == null ? PrimaryCheckbox( text: 'Habilitar inicio de sesión con correo (Opcional)', initialValue: enableLoginWithEmail, onChanged: (value) { setState(() { enableLoginWithEmail = value; }); }, ) : const SizedBox(), const SizedBox(height: 15), enableLoginWithEmail ? Container( decoration: BoxDecoration( border: Border.all( color: Colors.blue, width: 0.5, ), borderRadius: BorderRadius.circular(10), ), padding: const EdgeInsets.all(10), child: Column( children: [ TextFormField( controller: _emailController, validator: (String? value) { if (enableLoginWithEmail) { if (value == null || value.isEmpty) { return 'Por favor ingrese un email'; } final RegExp emailRegExp = RegExp( r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); if (!emailRegExp.hasMatch(value)) { return 'Por favor ingrese un email válido'; } return null; } else { return null; } }, decoration: const InputDecoration( prefixIcon: Icon(Icons.email_outlined), hintText: 'Email'), ), const SizedBox(height: 20.0), _email != null ? const SizedBox.shrink() : TextFormField( controller: _passwordController, obscureText: _obscureText, validator: (value) { if (enableLoginWithEmail) { if (value == null || value.isEmpty) { return 'Por favor ingrese una contraseña.'; } if (value.length < 5) { return 'Debe tener al menos 5 caracteres.'; } return null; } else { return null; } }, decoration: InputDecoration( prefixIcon: const Icon( Icons.lock_outline), suffixIcon: IconButton( icon: Icon( _obscureText ? Icons.visibility : Icons.visibility_off, color: Colors.grey, ), onPressed: () { setState(() { _obscureText = !_obscureText; }); }, ), hintText: 'Contraseña'), ), _email != null ? const SizedBox.shrink() : const SizedBox(height: 20), ], ), ) : const SizedBox(), ], ), ), ), Container( alignment: Alignment.bottomCenter, margin: const EdgeInsets.only(top: 35), padding: const EdgeInsets.only(bottom: 30), child: Column( children: [ _email != null ? PrimaryButton( onPressed: () { Navigator.push( context, CupertinoPageRoute( builder: (BuildContext context) { return NewPasswordScreen(); }, ), ); }, text: 'Cambiar Contraseña', minWidth: 300, minHeight: 45, ) : const SizedBox(height: 20), const SizedBox(height: 60), PrimaryButton( onPressed: () async { if (_formKey.currentState!.validate()) { await updateInfo(); } await userProvider.updateUserDataAndScores(); }, text: 'Guardar', ), ], ), ), ], ), ), ), ); } }