update
This commit is contained in:
@@ -0,0 +1,628 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:prosappco/src/authentication/authentication_repository.dart';
|
||||
import 'package:prosappco/src/components/photo_view_web.dart';
|
||||
import 'package:prosappco/src/components/pop_appbar.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:universal_html/html.dart' as html;
|
||||
|
||||
class ProfileWebScreen extends StatefulWidget {
|
||||
const ProfileWebScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ProfileWebScreen> createState() => _ProfileWebScreenState();
|
||||
}
|
||||
|
||||
class _ProfileWebScreenState extends State<ProfileWebScreen> {
|
||||
final uid = AuthenticationRepository.instance.getCurrentUserUid();
|
||||
final FirebaseStorage storage = FirebaseStorage.instance;
|
||||
late final FirebaseAuth _auth;
|
||||
List<City>? filteredCities;
|
||||
|
||||
// variables imagen
|
||||
String selectedImage = '';
|
||||
XFile? file;
|
||||
Uint8List? selectedImagInBytes;
|
||||
String photoTemp = '';
|
||||
|
||||
// controladores
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final TextEditingController _nameController = TextEditingController();
|
||||
final TextEditingController _emailController = TextEditingController();
|
||||
final TextEditingController _phoneNumberController = TextEditingController();
|
||||
final TextEditingController _passwordController = TextEditingController();
|
||||
bool _obscureText = true;
|
||||
|
||||
// variables
|
||||
String _ciudad = '...';
|
||||
String _photo = '...';
|
||||
String? _email = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final uid = AuthenticationRepository.instance.getCurrentUserUid();
|
||||
_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!;
|
||||
}
|
||||
|
||||
_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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
_selectFile(bool imageFrom) async {
|
||||
FilePickerResult? fileResult = await FilePicker.platform.pickFiles();
|
||||
|
||||
if (fileResult != null) {
|
||||
setState(() {
|
||||
selectedImage = fileResult.files.first.name;
|
||||
selectedImagInBytes = fileResult.files.first.bytes;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_uploadFile() async {
|
||||
try {
|
||||
final now = DateTime.now();
|
||||
final formattedDate = DateFormat('HHmmssddMMyyyy').format(now);
|
||||
final milliseconds = (now.microsecondsSinceEpoch / 1000).round();
|
||||
final random = '$formattedDate$milliseconds';
|
||||
|
||||
final Reference ref = FirebaseStorage.instance
|
||||
.ref()
|
||||
.child('users')
|
||||
.child(uid!)
|
||||
.child('profile')
|
||||
.child(random);
|
||||
|
||||
final metaData = SettableMetadata(contentType: 'image/jpeg');
|
||||
|
||||
final UploadTask uploadTask = ref.putData(selectedImagInBytes!, metaData);
|
||||
|
||||
final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true);
|
||||
|
||||
photoTemp = ref.fullPath;
|
||||
|
||||
if (snapshot.state == TaskState.success) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
print('web image error - $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> 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 (newName.isEmpty) {
|
||||
// Si el nuevo nombre está vacío, no lo actualizamos y mostramos un mensaje al usuario
|
||||
Get.snackbar(
|
||||
'Nombre Invalido',
|
||||
'Ingresa un nombre válido.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
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 (newEmail.isEmpty) {
|
||||
// Si el nuevo nombre está vacío, no lo actualizamos y mostramos un mensaje al usuario
|
||||
Get.snackbar(
|
||||
'Correo Invalido',
|
||||
'Ingresa un email válido.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
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 (currentUser?.email != newEmail) {
|
||||
if (newPassword.isNotEmpty) {
|
||||
await FirebaseFirestore.instance.collection('users').doc(uid).update({
|
||||
'email': newEmail,
|
||||
});
|
||||
updateEmailAndPassword(newEmail, newPassword);
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'Contraseña Invalida',
|
||||
'Por favor, ingresa una contraseña.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String? selectedCity = _ciudad;
|
||||
|
||||
// Guardar la ciudad seleccionada en el documento del usuario
|
||||
try {
|
||||
await FirebaseFirestore.instance.collection('users').doc(uid).update({
|
||||
'city': selectedCity,
|
||||
});
|
||||
} catch (e) {
|
||||
print('Error al actualizar la ciudad: $e');
|
||||
}
|
||||
|
||||
try {
|
||||
await FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(uid)
|
||||
.update({'phoneNumber': currentPhoneNumber});
|
||||
} catch (e) {
|
||||
print('e');
|
||||
}
|
||||
|
||||
try {
|
||||
await FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(uid)
|
||||
.update({'email': newEmail});
|
||||
} catch (e) {
|
||||
print('e');
|
||||
}
|
||||
|
||||
try {
|
||||
if (selectedImagInBytes == null) {
|
||||
return;
|
||||
} else {
|
||||
await _uploadFile();
|
||||
updateImage(photoTemp);
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error al actualizar la imagen de perfil $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> 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<void> updateEmailAndPassword(String email, String password) async {
|
||||
final User? user = FirebaseAuth.instance.currentUser;
|
||||
if (user != null) {
|
||||
try {
|
||||
await user.updateEmail(email);
|
||||
await user.updatePassword(password);
|
||||
} catch (e) {
|
||||
Get.snackbar(
|
||||
'Agregar correo',
|
||||
'Inicia sesión para asegurarnos de que seas tú.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
AuthenticationRepository.instance.logout(uid!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<City>> _getCities() async {
|
||||
List<City> citys = [];
|
||||
try {
|
||||
QuerySnapshot countries = await countriesCollection.get();
|
||||
for (DocumentSnapshot country in countries.docs) {
|
||||
String countryName = country.id;
|
||||
Map<String, dynamic> data = country.data() as Map<String, dynamic>;
|
||||
Map<String, Map<String, String>> states = {};
|
||||
|
||||
for (var entry in data.entries) {
|
||||
String key = entry.key;
|
||||
Map<String, String> cityData = Map<String, String>.from(entry.value);
|
||||
states[key] = cityData;
|
||||
}
|
||||
|
||||
for (var state in states.entries) {
|
||||
var citysState = state.value.entries.map((city) => City(
|
||||
cityName: city.key,
|
||||
coordsOfCity: city.value,
|
||||
stateOfCity: state.key,
|
||||
countryOfCity: countryName,
|
||||
));
|
||||
|
||||
citys.addAll(citysState);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error obteniendo las ciudades: $e');
|
||||
}
|
||||
return citys;
|
||||
}
|
||||
|
||||
Future<void> _showEmailUpdateDialog(BuildContext context) async {
|
||||
TextEditingController emailController = TextEditingController();
|
||||
String currentEmail = FirebaseAuth.instance.currentUser?.email ?? '';
|
||||
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Actualizar Correo Electrónico'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
controller: emailController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Nuevo Correo Electrónico',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Cancelar'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
String newEmail = emailController.text.trim();
|
||||
if (newEmail.isNotEmpty && newEmail != currentEmail) {
|
||||
// Actualiza el correo electrónico aquí
|
||||
try {
|
||||
await FirebaseAuth.instance.currentUser
|
||||
?.updateEmail(newEmail);
|
||||
await FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(FirebaseAuth.instance.currentUser?.uid)
|
||||
.update({'email': newEmail});
|
||||
Navigator.of(context).pop();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content:
|
||||
Text('Correo Electrónico actualizado con éxito'),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content:
|
||||
Text('Error al actualizar el correo electrónico'),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content:
|
||||
Text('Ingresa un nuevo correo electrónico válido'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue,
|
||||
),
|
||||
child: const Text('Guardar'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFD6F4FF),
|
||||
appBar: PopAppbar(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
label: 'Perfil'),
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
width: 300,
|
||||
height: 680,
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
color: Colors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 20, bottom: 20),
|
||||
child: (selectedImagInBytes != null)
|
||||
? LocalPhotoWeb(file: selectedImagInBytes)
|
||||
: ReferencePhotoWeb(ref: storage.ref().child(_photo)),
|
||||
),
|
||||
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)',
|
||||
),
|
||||
),
|
||||
_email != null
|
||||
? TextFormField(
|
||||
onTap: () {
|
||||
_showEmailUpdateDialog(context);
|
||||
},
|
||||
readOnly: true,
|
||||
controller: _emailController,
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: Icon(Icons.email_outlined),
|
||||
hintText: 'Email (Obligatorio)',
|
||||
),
|
||||
)
|
||||
: TextFormField(
|
||||
controller: _emailController,
|
||||
validator: (String? value) {
|
||||
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;
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: Icon(Icons.email_outlined),
|
||||
hintText: 'Email (Obligatorio)',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
_email != null
|
||||
? const SizedBox.shrink()
|
||||
: TextFormField(
|
||||
controller: _passwordController,
|
||||
obscureText: _obscureText,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Porfavor ingrese una contraseña.';
|
||||
}
|
||||
if (value.length < 5) {
|
||||
return 'Debe tener al menos 5 caracteres.';
|
||||
}
|
||||
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 (Obligatorio)',
|
||||
),
|
||||
),
|
||||
_email != null
|
||||
? const SizedBox.shrink()
|
||||
: const SizedBox(height: 20.0),
|
||||
FutureBuilder<List<City>>(
|
||||
future: _getCities(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.waiting) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return const Center(
|
||||
child: Text('Error al obtener las ciudades'),
|
||||
);
|
||||
} else {
|
||||
List<City> filteredCities = snapshot.data!;
|
||||
|
||||
return DropdownButtonFormField<String>(
|
||||
value: _ciudad,
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
_ciudad = newValue!;
|
||||
});
|
||||
},
|
||||
items: filteredCities.map((City city) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: city.cityName,
|
||||
child: Text(city.cityName ?? ''),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.near_me),
|
||||
hintText: _ciudad,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
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'),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
margin: const EdgeInsets.only(top: 30),
|
||||
padding: const EdgeInsets.only(bottom: 30),
|
||||
child: Column(
|
||||
children: [
|
||||
_email != null
|
||||
? ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
CupertinoPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return NewPasswordScreen();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2BA4EC),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
elevation: 0,
|
||||
minimumSize: const Size(300, 45),
|
||||
),
|
||||
child: const Text(
|
||||
'Cambiar Contraseña',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(height: 20),
|
||||
const SizedBox(height: 60),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
await updateInfo().whenComplete(() {
|
||||
html.window.location.reload();
|
||||
|
||||
Get.snackbar(
|
||||
'Perfil',
|
||||
'Información actualizada correctamente.',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2BA4EC),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
elevation: 0,
|
||||
minimumSize: const Size(200, 50),
|
||||
),
|
||||
child: const Text(
|
||||
'Guardar',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user