diff --git a/lib/ui/views/login_view.dart b/lib/ui/views/login_view.dart index 95b13d6..b3e12c7 100644 --- a/lib/ui/views/login_view.dart +++ b/lib/ui/views/login_view.dart @@ -12,144 +12,168 @@ class LoginView extends StatelessWidget { Widget build(BuildContext context) { final authProvider = Provider.of(context); final isDark = Theme.of(context).brightness == Brightness.dark; - final titleColor = isDark ? Colors.white : const Color(0xFF1E293B); - final subtitleColor = isDark ? Colors.white54 : Colors.grey; - final inputFill = isDark ? const Color(0xFF1E293B) : const Color(0xFFF5F8FF); - final inputBorder = isDark ? const Color(0xFF334155) : const Color(0xFFE0E7FF); + final cardColor = isDark ? const Color(0xFF1E293B) : Colors.white; + final titleColor = isDark ? Colors.white : const Color(0xFF0F172A); + final subtitleColor = isDark ? const Color(0xFF94A3B8) : const Color(0xFF64748B); + final inputFill = isDark ? const Color(0xFF0F172A) : const Color(0xFFF8FAFF); + final inputBorder = isDark ? const Color(0xFF334155) : const Color(0xFFCDD5F0); + final inputTextColor = isDark ? Colors.white : const Color(0xFF0F172A); + + InputDecoration inputDecoration(String label, String hint, IconData icon) { + return InputDecoration( + labelText: label, + labelStyle: TextStyle(color: subtitleColor), + hintText: hint, + hintStyle: TextStyle(color: subtitleColor), + prefixIcon: Icon(icon, color: const Color(0xFF42A4EF)), + filled: true, + fillColor: inputFill, + border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: inputBorder)), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFF42A4EF), width: 2)), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Colors.red)), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Colors.red, width: 2)), + ); + } return ChangeNotifierProvider( create: (_) => LoginFormProvider(), child: Builder(builder: (context) { final loginFormProvider = Provider.of(context, listen: false); - InputDecoration inputDecoration(String label, String hint, IconData icon) { - return InputDecoration( - labelText: label, - hintText: hint, - prefixIcon: const Icon(Icons.email_outlined, color: Color(0xFF42A4EF)), - filled: true, - fillColor: inputFill, - border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: inputBorder)), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide(color: Color(0xFF42A4EF), width: 2)), - errorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide(color: Colors.red)), - focusedErrorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide(color: Colors.red, width: 2)), - ); - } - return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 16), - child: Form( - autovalidateMode: AutovalidateMode.onUserInteraction, - key: loginFormProvider.formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Iniciar sesión', - style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: titleColor)), - const SizedBox(height: 4), - Text('Ingresa con tu cuenta de email', - style: TextStyle(color: subtitleColor, fontSize: 13)), - const SizedBox(height: 28), - - TextFormField( - keyboardType: TextInputType.emailAddress, - onFieldSubmitted: (_) => _submit(loginFormProvider, authProvider), - validator: (v) => EmailValidator.validate(v ?? '') ? null : 'Email no válido', - onChanged: (v) => loginFormProvider.email = v, - style: TextStyle(color: titleColor), - decoration: inputDecoration('Email', 'correo@ejemplo.com', Icons.email_outlined), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 32), + child: Container( + decoration: BoxDecoration( + color: cardColor, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(isDark ? 0.3 : 0.08), + blurRadius: 24, + offset: const Offset(0, 4), ), - const SizedBox(height: 16), - - TextFormField( - obscureText: true, - onFieldSubmitted: (_) => _submit(loginFormProvider, authProvider), - validator: (v) { - if (v == null || v.isEmpty) return 'Ingresa tu contraseña'; - if (v.length < 6) return 'Mínimo 6 caracteres'; - return null; - }, - onChanged: (v) => loginFormProvider.password = v, - style: TextStyle(color: titleColor), - decoration: inputDecoration('Contraseña', '••••••••', Icons.lock_outline), - ), - const SizedBox(height: 24), - - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: () => _submit(loginFormProvider, authProvider), - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF42A4EF), - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - elevation: 0, - ), - child: const Text('Ingresar', - style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600)), + ], + ), + padding: const EdgeInsets.all(28), + child: Form( + autovalidateMode: AutovalidateMode.onUserInteraction, + key: loginFormProvider.formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Iniciar sesión', + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: titleColor), ), - ), - const SizedBox(height: 24), + const SizedBox(height: 4), + Text( + 'Ingresa con tu cuenta de email', + style: TextStyle(color: subtitleColor, fontSize: 13), + ), + const SizedBox(height: 28), - Row(children: [ - const Expanded(child: Divider()), - Padding( + TextFormField( + keyboardType: TextInputType.emailAddress, + onFieldSubmitted: (_) => _submit(loginFormProvider, authProvider), + validator: (v) => EmailValidator.validate(v ?? '') ? null : 'Email no válido', + onChanged: (v) => loginFormProvider.email = v, + style: TextStyle(color: inputTextColor), + decoration: inputDecoration('Email', 'correo@ejemplo.com', Icons.email_outlined), + ), + const SizedBox(height: 16), + + TextFormField( + obscureText: true, + onFieldSubmitted: (_) => _submit(loginFormProvider, authProvider), + validator: (v) { + if (v == null || v.isEmpty) return 'Ingresa tu contraseña'; + if (v.length < 6) return 'Mínimo 6 caracteres'; + return null; + }, + onChanged: (v) => loginFormProvider.password = v, + style: TextStyle(color: inputTextColor), + decoration: inputDecoration('Contraseña', '••••••••', Icons.lock_outline), + ), + const SizedBox(height: 24), + + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () => _submit(loginFormProvider, authProvider), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF42A4EF), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + elevation: 0, + ), + child: const Text('Ingresar', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600)), + ), + ), + const SizedBox(height: 24), + + Row(children: [ + const Expanded(child: Divider()), + Padding( padding: const EdgeInsets.symmetric(horizontal: 12), - child: Text('o', style: TextStyle(color: subtitleColor))), - const Expanded(child: Divider()), - ]), - const SizedBox(height: 16), - - SizedBox( - width: double.infinity, - child: OutlinedButton.icon( - onPressed: () => - Navigator.pushReplacementNamed(context, Flurorouter.phoneLoginRoute), - icon: const Icon(Icons.phone_android, color: Color(0xFF42A4EF)), - label: const Text('Ingresar con celular', - style: TextStyle(color: Color(0xFF42A4EF), fontWeight: FontWeight.w600)), - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 14), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - side: const BorderSide(color: Color(0xFF42A4EF)), + child: Text('o', style: TextStyle(color: subtitleColor)), ), - ), - ), - const SizedBox(height: 12), + const Expanded(child: Divider()), + ]), + const SizedBox(height: 16), - Center( - child: TextButton( - onPressed: () => - Navigator.pushReplacementNamed(context, Flurorouter.registerRoute), - child: RichText( - text: TextSpan( - style: TextStyle(color: subtitleColor, fontSize: 13), - children: const [ - TextSpan(text: '¿No tienes cuenta? '), - TextSpan( - text: 'Regístrate', - style: TextStyle( - color: Color(0xFF42A4EF), fontWeight: FontWeight.w600), - ), - ], + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: () => Navigator.pushReplacementNamed( + context, Flurorouter.phoneLoginRoute), + icon: const Icon(Icons.phone_android, color: Color(0xFF42A4EF)), + label: const Text('Ingresar con celular', + style: TextStyle( + color: Color(0xFF42A4EF), fontWeight: FontWeight.w600)), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + side: const BorderSide(color: Color(0xFF42A4EF)), ), ), ), - ), - ], + const SizedBox(height: 12), + + Center( + child: TextButton( + onPressed: () => Navigator.pushReplacementNamed( + context, Flurorouter.registerRoute), + child: RichText( + text: TextSpan( + style: TextStyle(color: subtitleColor, fontSize: 13), + children: const [ + TextSpan(text: '¿No tienes cuenta? '), + TextSpan( + text: 'Regístrate', + style: TextStyle( + color: Color(0xFF42A4EF), fontWeight: FontWeight.w600), + ), + ], + ), + ), + ), + ), + ], + ), ), ), ), diff --git a/lib/ui/views/phone_login_view.dart b/lib/ui/views/phone_login_view.dart index 532cd74..55801b4 100644 --- a/lib/ui/views/phone_login_view.dart +++ b/lib/ui/views/phone_login_view.dart @@ -52,7 +52,7 @@ class _PhoneLoginViewState extends State { setState(() => _loading = true); try { await Provider.of(context, listen: false).verifyPhoneNumber(_phone); - setState(() { _codeSent = true; }); + setState(() => _codeSent = true); _startResendTimer(); } catch (_) { ScaffoldMessenger.of(context).showSnackBar( @@ -78,38 +78,70 @@ class _PhoneLoginViewState extends State { @override Widget build(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; + final cardColor = isDark ? const Color(0xFF1E293B) : Colors.white; + final titleColor = isDark ? Colors.white : const Color(0xFF0F172A); + final subtitleColor = isDark ? const Color(0xFF94A3B8) : const Color(0xFF64748B); + final inputFill = isDark ? const Color(0xFF0F172A) : const Color(0xFFF8FAFF); + final inputBorder = isDark ? const Color(0xFF334155) : const Color(0xFFCDD5F0); + final inputTextColor = isDark ? Colors.white : const Color(0xFF0F172A); + final prefixFill = isDark ? const Color(0xFF0F172A) : const Color(0xFFEEF0FF); + final prefixBorder = isDark ? const Color(0xFF334155) : const Color(0xFFCDD5F0); + return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 420), child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (!_codeSent) _buildPhoneForm() else _buildOtpForm(), - ], + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 32), + child: Container( + decoration: BoxDecoration( + color: cardColor, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(isDark ? 0.3 : 0.08), + blurRadius: 24, + offset: const Offset(0, 4), + ), + ], + ), + padding: const EdgeInsets.all(28), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (!_codeSent) + _buildPhoneForm(titleColor, subtitleColor, inputFill, inputBorder, inputTextColor, prefixFill, prefixBorder) + else + _buildOtpForm(titleColor, subtitleColor, inputFill, inputBorder, inputTextColor), + ], + ), ), ), ), ); } - Widget _buildPhoneForm() { - final isDark = Theme.of(context).brightness == Brightness.dark; - final titleColor = isDark ? Colors.white : const Color(0xFF1E293B); - final subtitleColor = isDark ? Colors.white54 : Colors.grey; - final inputFill = isDark ? const Color(0xFF1E293B) : const Color(0xFFF5F8FF); - final inputBorder = isDark ? const Color(0xFF334155) : const Color(0xFFE0E7FF); - final prefixFill = isDark ? const Color(0xFF0F172A) : const Color(0xFFF5F5F5); - final prefixBorder = isDark ? const Color(0xFF334155) : const Color(0xFFE0E0E0); - final prefixText = isDark ? Colors.white : Colors.black87; - + Widget _buildPhoneForm( + Color titleColor, + Color subtitleColor, + Color inputFill, + Color inputBorder, + Color inputTextColor, + Color prefixFill, + Color prefixBorder, + ) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Iniciar sesión', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: titleColor)), + Text( + 'Iniciar sesión', + style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: titleColor), + ), const SizedBox(height: 4), - Text('Te enviaremos un código de 6 dígitos', style: TextStyle(color: subtitleColor, fontSize: 13)), + Text( + 'Te enviaremos un código de 6 dígitos', + style: TextStyle(color: subtitleColor, fontSize: 13), + ), const SizedBox(height: 24), Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -121,14 +153,17 @@ class _PhoneLoginViewState extends State { borderRadius: BorderRadius.circular(10), border: Border.all(color: prefixBorder), ), - child: Text('+57', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: prefixText)), + child: Text( + '+57', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: titleColor), + ), ), const SizedBox(width: 10), Expanded( child: TextField( controller: _phoneController, keyboardType: TextInputType.phone, - style: TextStyle(color: titleColor), + style: TextStyle(color: inputTextColor, fontSize: 15), decoration: InputDecoration( hintText: '300 123 4567', hintStyle: TextStyle(color: subtitleColor), @@ -187,7 +222,10 @@ class _PhoneLoginViewState extends State { shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), side: const BorderSide(color: Color(0xFF42A4EF)), ), - child: const Text('Iniciar sesión con email', style: TextStyle(color: Color(0xFF42A4EF), fontWeight: FontWeight.w600)), + child: const Text( + 'Iniciar sesión con email', + style: TextStyle(color: Color(0xFF42A4EF), fontWeight: FontWeight.w600), + ), ), ), const SizedBox(height: 12), @@ -199,7 +237,10 @@ class _PhoneLoginViewState extends State { style: TextStyle(color: subtitleColor, fontSize: 13), children: const [ TextSpan(text: '¿No tienes cuenta? '), - TextSpan(text: 'Regístrate', style: TextStyle(color: Color(0xFF42A4EF), fontWeight: FontWeight.w600)), + TextSpan( + text: 'Regístrate', + style: TextStyle(color: Color(0xFF42A4EF), fontWeight: FontWeight.w600), + ), ], ), ), @@ -209,19 +250,22 @@ class _PhoneLoginViewState extends State { ); } - Widget _buildOtpForm() { - final isDark = Theme.of(context).brightness == Brightness.dark; - final titleColor = isDark ? Colors.white : const Color(0xFF1E293B); - final subtitleColor = isDark ? Colors.white54 : Colors.grey; - final inputFill = isDark ? const Color(0xFF1E293B) : const Color(0xFFF5F8FF); - final inputBorder = isDark ? const Color(0xFF334155) : const Color(0xFFE0E7FF); - + Widget _buildOtpForm( + Color titleColor, + Color subtitleColor, + Color inputFill, + Color inputBorder, + Color inputTextColor, + ) { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const Icon(Icons.sms_outlined, size: 52, color: Color(0xFF42A4EF)), const SizedBox(height: 12), - Text('Código de verificación', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: titleColor)), + Text( + 'Código de verificación', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: titleColor), + ), const SizedBox(height: 8), Text( 'Enviamos un SMS a $_phone', @@ -234,7 +278,12 @@ class _PhoneLoginViewState extends State { keyboardType: TextInputType.number, textAlign: TextAlign.center, maxLength: 6, - style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, letterSpacing: 12, color: titleColor), + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + letterSpacing: 12, + color: inputTextColor, + ), decoration: InputDecoration( counterText: '', hintText: '------', @@ -242,8 +291,11 @@ class _PhoneLoginViewState extends State { filled: true, fillColor: inputFill, border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none), - enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: inputBorder)), - focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFF42A4EF), width: 2)), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: inputBorder)), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: Color(0xFF42A4EF), width: 2)), ), ), const SizedBox(height: 24), @@ -268,11 +320,15 @@ class _PhoneLoginViewState extends State { ? Text('Reenviar en $_resendSeconds s', style: TextStyle(color: subtitleColor, fontSize: 13)) : TextButton( onPressed: _sendCode, - child: const Text('Reenviar código', style: TextStyle(color: Color(0xFF42A4EF), fontWeight: FontWeight.w600)), + child: const Text('Reenviar código', + style: TextStyle(color: Color(0xFF42A4EF), fontWeight: FontWeight.w600)), ), const SizedBox(height: 8), TextButton( - onPressed: () => setState(() { _codeSent = false; _otpController.clear(); }), + onPressed: () => setState(() { + _codeSent = false; + _otpController.clear(); + }), child: Text('← Cambiar número', style: TextStyle(color: subtitleColor, fontSize: 13)), ), ],