perfil_screen.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import 'package:flutter/material.dart';
  2. import 'package:yoshi_papas_app/widgets/widgets.dart';
  3. import 'package:provider/provider.dart';
  4. import '../../themes/themes.dart';
  5. import '../../viewmodels/viewmodels.dart';
  6. class PerfilScreen extends StatefulWidget {
  7. const PerfilScreen({Key? key}) : super(key: key);
  8. @override
  9. State<PerfilScreen> createState() => _PerfilState();
  10. }
  11. class _PerfilState extends State<PerfilScreen> {
  12. final _passActualController = TextEditingController();
  13. final _passNuevoController = TextEditingController();
  14. final _passNuevoConfirmarController = TextEditingController();
  15. bool _isChangingPassword = false;
  16. @override
  17. void initState() {
  18. super.initState();
  19. final viewModel = Provider.of<ProfileViewModel>(context, listen: false);
  20. Future(() async {
  21. viewModel.setLoading(true);
  22. await viewModel.fetchProfile();
  23. viewModel.setLoading(false);
  24. });
  25. if (!mounted) return;
  26. }
  27. @override
  28. void dispose() {
  29. // Desecha los controladores cuando ya no sean necesarios
  30. _passActualController.dispose();
  31. _passNuevoController.dispose();
  32. _passNuevoConfirmarController.dispose();
  33. super.dispose();
  34. }
  35. @override
  36. Widget build(BuildContext context) {
  37. final profileViewModel = Provider.of<ProfileViewModel>(context);
  38. final perfil = profileViewModel.profile;
  39. final isLoading = profileViewModel.isLoading;
  40. final loginViewModel = Provider.of<LoginViewModel>(context);
  41. final errores = loginViewModel.errores;
  42. return Scaffold(
  43. appBar: AppBar(
  44. title: const Text(
  45. 'Perfil',
  46. style: TextStyle(color: Colors.black),
  47. ),
  48. ),
  49. body: isLoading
  50. ? const Center(
  51. child: CircularProgressIndicator(),
  52. )
  53. : SingleChildScrollView(
  54. child: Center(
  55. child: Column(
  56. mainAxisAlignment: MainAxisAlignment.center,
  57. children: [
  58. const SizedBox(
  59. height: 15,
  60. ),
  61. //Nombre de usuario
  62. Text(
  63. '${perfil.nombre}',
  64. style: const TextStyle(
  65. fontSize: 20,
  66. fontWeight: FontWeight.bold,
  67. ),
  68. ),
  69. const SizedBox(
  70. height: 10,
  71. ),
  72. const Padding(
  73. padding: EdgeInsets.symmetric(horizontal: 20),
  74. child: Divider(thickness: 1.2),
  75. ),
  76. const SizedBox(
  77. height: 10,
  78. ),
  79. //Nombre completo de usuario
  80. ListTile(
  81. leading: const Icon(Icons.person_pin_rounded),
  82. title: Text('${perfil.nombre}'),
  83. ),
  84. //Email de usuario
  85. ListTile(
  86. leading: const Icon(Icons.email),
  87. title: Text('${perfil.correo}'),
  88. ),
  89. const Padding(
  90. padding: EdgeInsets.symmetric(horizontal: 20),
  91. child: Divider(thickness: 1.2),
  92. ),
  93. Padding(
  94. padding: const EdgeInsets.all(15),
  95. child: Column(
  96. children: [
  97. //CONTRASEÑA
  98. AppTextField(
  99. autofillHints: [AutofillHints.newPassword],
  100. maxLines: 1,
  101. controller:
  102. _passActualController, // Usa el controlador de estado
  103. obscureText:
  104. profileViewModel.obscureTextPassActual,
  105. prefixIcon: const Icon(Icons.lock),
  106. suffixIcon: IconButton(
  107. icon: Icon(
  108. profileViewModel.obscureTextPassActual
  109. ? Icons.visibility_off
  110. : Icons.visibility,
  111. ),
  112. onPressed: () => profileViewModel
  113. .toggleObscureTextPassActual(),
  114. ),
  115. labelText: 'Contraseña Actual',
  116. hintText: 'Introduzca su contraseña actual',
  117. ),
  118. const SizedBox(
  119. height: 15,
  120. ),
  121. AppTextField(
  122. autofillHints: [AutofillHints.newPassword],
  123. maxLines: 1,
  124. controller:
  125. _passNuevoController, // Usa el controlador de estado
  126. obscureText:
  127. profileViewModel.obscureTextPassNuevo,
  128. prefixIcon: const Icon(Icons.lock),
  129. suffixIcon: IconButton(
  130. icon: Icon(
  131. profileViewModel.obscureTextPassNuevo
  132. ? Icons.visibility_off
  133. : Icons.visibility,
  134. ),
  135. onPressed: () => profileViewModel
  136. .toggleObscureTextPassNuevo(),
  137. ),
  138. labelText: 'Nueva Contraseña',
  139. hintText: 'Introduzca su nueva contraseña',
  140. ),
  141. const SizedBox(
  142. height: 15,
  143. ),
  144. AppTextField(
  145. autofillHints: [AutofillHints.newPassword],
  146. maxLines: 1,
  147. controller:
  148. _passNuevoConfirmarController, // Usa el controlador de estado
  149. obscureText: profileViewModel
  150. .obscureTextPassNuevoConfirmar,
  151. prefixIcon: const Icon(Icons.lock),
  152. suffixIcon: IconButton(
  153. icon: Icon(
  154. profileViewModel.obscureTextPassNuevoConfirmar
  155. ? Icons.visibility_off
  156. : Icons.visibility,
  157. ),
  158. onPressed: () => profileViewModel
  159. .toggleObscureTextPassNuevoConfirmar(),
  160. ),
  161. labelText: 'Confirmar Nueva Contraseña',
  162. hintText: 'Confirme su nueva contraseña',
  163. ),
  164. const SizedBox(
  165. height: 20,
  166. ),
  167. Align(
  168. alignment: Alignment.topLeft,
  169. child: SizedBox(
  170. height: 75,
  171. width: 380,
  172. child: ElevatedButton(
  173. style: ButtonStyle(
  174. shape: MaterialStatePropertyAll(
  175. RoundedRectangleBorder(
  176. borderRadius: BorderRadius.circular(10),
  177. ),
  178. ),
  179. backgroundColor: MaterialStatePropertyAll(
  180. AppTheme.primary),
  181. ),
  182. onPressed: _isChangingPassword
  183. ? null // Deshabilita el botón si la solicitud está en progreso
  184. : () async {
  185. // Verificar que la contraseña nueva no sea igual a la contraseña actual
  186. if (_passActualController.text ==
  187. _passNuevoController.text) {
  188. ScaffoldMessenger.of(context)
  189. .showSnackBar(
  190. const SnackBar(
  191. content: Text(
  192. 'La nueva contraseña debe ser diferente a la actual')),
  193. );
  194. return;
  195. }
  196. // Verificar que la nueva contraseña y la confirmación sean iguales
  197. if (_passNuevoController.text !=
  198. _passNuevoConfirmarController
  199. .text) {
  200. ScaffoldMessenger.of(context)
  201. .showSnackBar(
  202. const SnackBar(
  203. content: Text(
  204. 'La nueva contraseña y la confirmación no coinciden')),
  205. );
  206. return;
  207. }
  208. setState(() {
  209. _isChangingPassword =
  210. true; // Indicar que el cambio de contraseña está en progreso.
  211. });
  212. // Intentar cambiar la contraseña llamando al método correspondiente en el ViewModel.
  213. final cambioExitoso =
  214. await profileViewModel
  215. .cambiarContrasena(
  216. _passActualController.text,
  217. _passNuevoController.text,
  218. );
  219. // Regresar a la pantalla anterior sólo si el cambio fue exitoso.
  220. if (cambioExitoso && mounted) {
  221. // Si quieres mostrar un mensaje de éxito antes de salir, puedes usar un SnackBar aquí.
  222. ScaffoldMessenger.of(context)
  223. .showSnackBar(
  224. const SnackBar(
  225. content: Text(
  226. 'Contraseña cambiada con éxito')),
  227. );
  228. // Usar un delay para dar tiempo al SnackBar a que se muestre antes de salir.
  229. await Future.delayed(
  230. const Duration(seconds: 2));
  231. Navigator.of(context)
  232. .pop(); // Salir de la pantalla actual.
  233. } else {
  234. // Si el cambio no fue exitoso, mostrar un SnackBar con el mensaje de error.
  235. ScaffoldMessenger.of(context)
  236. .showSnackBar(
  237. const SnackBar(
  238. content: Text(
  239. 'Ocurrió un error al guardar el perfil')),
  240. );
  241. }
  242. // Permitir más cambios de contraseña al restablecer el estado.
  243. if (mounted) {
  244. setState(() {
  245. _isChangingPassword = false;
  246. });
  247. }
  248. },
  249. child: Row(
  250. mainAxisAlignment: MainAxisAlignment.center,
  251. children: [
  252. Text('Guardar',
  253. style: TextStyle(
  254. fontSize: 18,
  255. color: AppTheme.secondary)),
  256. ],
  257. ),
  258. ),
  259. ),
  260. ),
  261. ],
  262. ))
  263. ],
  264. ),
  265. ),
  266. ),
  267. );
  268. }
  269. }