From c91d1f298eb9ec66ea4ada2ec627db7eb3d4282e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mois=C3=A9s=20M=C3=A9ndez?= Date: Sat, 23 May 2026 03:41:03 -0600 Subject: [PATCH] fix: actualizar la forma de aplicar opacidad en colores de tema y componentes de la interfaz, y se arreglaron errores de interfaz --- lib/core/theme/app_theme.dart | 6 +- .../screens/home_screen_placeholder.dart | 10 +- .../presentation/screens/login_screen.dart | 314 +++++++++--------- .../presentation/screens/register_screen.dart | 2 +- pubspec.lock | 102 ++++-- 5 files changed, 243 insertions(+), 191 deletions(-) diff --git a/lib/core/theme/app_theme.dart b/lib/core/theme/app_theme.dart index 23110a2..79d6cda 100644 --- a/lib/core/theme/app_theme.dart +++ b/lib/core/theme/app_theme.dart @@ -55,7 +55,7 @@ abstract final class AppTheme { borderRadius: BorderRadius.circular(12), ), elevation: 3, - shadowColor: leafGreen.withOpacity(0.4), + shadowColor: leafGreen.withValues(alpha: 0.4), textStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, @@ -89,12 +89,12 @@ abstract final class AppTheme { borderSide: const BorderSide(color: errorRed, width: 2), ), labelStyle: const TextStyle(color: midGray), - hintStyle: TextStyle(color: midGray.withOpacity(0.7)), + hintStyle: TextStyle(color: midGray.withValues(alpha: 0.7)), ), cardTheme: CardThemeData( color: Colors.white, elevation: 2, - shadowColor: Colors.black.withOpacity(0.08), + shadowColor: Colors.black.withValues(alpha: 0.08), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), diff --git a/lib/features/auth/presentation/screens/home_screen_placeholder.dart b/lib/features/auth/presentation/screens/home_screen_placeholder.dart index d3e844e..5ee44a3 100644 --- a/lib/features/auth/presentation/screens/home_screen_placeholder.dart +++ b/lib/features/auth/presentation/screens/home_screen_placeholder.dart @@ -122,7 +122,7 @@ class _WelcomeCard extends StatelessWidget { borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( - color: AppTheme.leafGreen.withOpacity(0.3), + color: AppTheme.leafGreen.withValues(alpha: 0.3), blurRadius: 16, offset: const Offset(0, 6), ), @@ -134,7 +134,7 @@ class _WelcomeCard extends StatelessWidget { width: 52, height: 52, decoration: BoxDecoration( - color: Colors.white.withOpacity(0.2), + color: Colors.white.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(13), ), child: const Icon( @@ -169,7 +169,7 @@ class _WelcomeCard extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 3), decoration: BoxDecoration( - color: Colors.white.withOpacity(0.2), + color: Colors.white.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(20), ), child: Text( @@ -307,10 +307,10 @@ class _PreventiveMessageCard extends StatelessWidget { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppTheme.alertAmber.withOpacity(0.08), + color: AppTheme.alertAmber.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(14), border: Border.all( - color: AppTheme.alertAmber.withOpacity(0.3), + color: AppTheme.alertAmber.withValues(alpha: 0.3), ), ), child: Column( diff --git a/lib/features/auth/presentation/screens/login_screen.dart b/lib/features/auth/presentation/screens/login_screen.dart index 384b7d4..7cbf586 100644 --- a/lib/features/auth/presentation/screens/login_screen.dart +++ b/lib/features/auth/presentation/screens/login_screen.dart @@ -8,15 +8,6 @@ import '../bloc/auth_event.dart'; import '../bloc/auth_state.dart'; import '../widgets/privacy_notice_card.dart'; -/// Pantalla de inicio de sesión — MVP WasteNotify. -/// -/// Diseño: Limpio, institucional, con paleta verde-tierra. -/// Incluye mensajería preventiva integrada de forma no intrusiva. -/// -/// Credenciales de demo: -/// Ciudadano: ciudadano@ejemplo.com / password123 -/// Operador: operador@ejemplo.com / operador456 -/// Teléfono: 5551234567 / pass1234 class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @@ -31,6 +22,7 @@ class _LoginScreenState extends State final _passwordController = TextEditingController(); bool _obscurePassword = true; + late final AnimationController _animController; late final Animation _fadeAnim; late final Animation _slideAnim; @@ -42,17 +34,9 @@ class _LoginScreenState extends State vsync: this, duration: const Duration(milliseconds: 700), ); - _fadeAnim = CurvedAnimation( - parent: _animController, - curve: Curves.easeOut, - ); - _slideAnim = Tween( - begin: const Offset(0, 0.06), - end: Offset.zero, - ).animate(CurvedAnimation( - parent: _animController, - curve: Curves.easeOutCubic, - )); + _fadeAnim = CurvedAnimation(parent: _animController, curve: Curves.easeOut); + _slideAnim = Tween(begin: const Offset(0, 0.06), end: Offset.zero) + .animate(CurvedAnimation(parent: _animController, curve: Curves.easeOutCubic)); _animController.forward(); } @@ -68,7 +52,7 @@ class _LoginScreenState extends State if (_formKey.currentState?.validate() ?? false) { context.read().add( AuthLoginRequested( - identifier: _identifierController.text, + identifier: _identifierController.text.trim(), password: _passwordController.text, ), ); @@ -113,151 +97,179 @@ class _LoginScreenState extends State const SizedBox(height: 48), _buildHeader(), const SizedBox(height: 32), - const ScheduleWarningBanner(), - const SizedBox(height: 32), _buildForm(context), const SizedBox(height: 24), const PrivacyNoticeCard(), const SizedBox(height: 32), _buildDemoHint(), - const SizedBox(height: 24), + const Spacer(), + const SizedBox(height: 8), + Text( + 'Sistema Municipal de Recolección Residencial\nCelaya, Guanajuato', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade600, + height: 1.3), + ), + const SizedBox(height: 48), ], ), ), - ), const SizedBox(height: 8), - Text( - 'Sistema Municipal de Recolección Residencial\nCelaya, Guanajuato', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, color: Colors.grey.shade600, height: 1.3), ), - const SizedBox(height: 48), + ), + ), + ], + ), + ), + ), + ); + } - // --- CAMPO: EMAIL / IDENTIFICADOR --- - TextFormField( - controller: _emailController, - keyboardType: TextInputType.emailAddress, - decoration: const InputDecoration( - labelText: 'Correo Electrónico o Teléfono', - prefixIcon: Icon(Icons.email_outlined), - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(12))), - ), - validator: (value) => - (value == null || value.trim().isEmpty) - ? 'Por favor, ingresa tus datos de acceso' - : null, - ), - const SizedBox(height: 20), + Widget _buildHeader() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text( + 'Bienvenido a WasteNotify', + style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold), + ), + SizedBox(height: 8), + Text( + 'Inicia sesión para continuar', + style: TextStyle(fontSize: 14, color: Colors.grey), + ), + ], + ); + } - // --- CAMPO: CONTRASENA --- - TextFormField( - controller: _passwordController, - obscureText: true, - decoration: const InputDecoration( - labelText: 'Contraseña de Acceso', - prefixIcon: Icon(Icons.lock_outline_rounded), - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(12))), - ), - validator: (value) => (value == null || value.isEmpty) - ? 'Por favor, introduce tu contraseña' - : null, - ), - const SizedBox(height: 32), + Widget _buildForm(BuildContext context) { + return Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextFormField( + controller: _identifierController, + keyboardType: TextInputType.emailAddress, + decoration: const InputDecoration( + labelText: 'Correo Electrónico o Teléfono', + prefixIcon: Icon(Icons.email_outlined), + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(12))), + ), + validator: (value) => (value == null || value.trim().isEmpty) + ? 'Por favor, ingresa tus datos de acceso' + : null, + ), + const SizedBox(height: 20), - // --- CONTENEDOR REACTIVO DE BOTONES (LOGIN & REGISTER) --- - // --- CONTENEDOR REACTIVO DE BOTONES (LOGIN & REGISTER) --- - BlocBuilder( - builder: (context, state) { - final isLoading = state is AuthLoading; + TextFormField( + controller: _passwordController, + obscureText: _obscurePassword, + decoration: InputDecoration( + labelText: 'Contraseña de Acceso', + prefixIcon: const Icon(Icons.lock_outline_rounded), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(12))), + suffixIcon: IconButton( + icon: Icon( + _obscurePassword ? Icons.visibility : Icons.visibility_off), + onPressed: () => setState(() => _obscurePassword = !_obscurePassword), + ), + ), + validator: (value) => (value == null || value.isEmpty) + ? 'Por favor, introduce tu contraseña' + : null, + ), + const SizedBox(height: 32), - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - AnimatedContainer( - duration: const Duration(milliseconds: 200), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - boxShadow: isLoading - ? [] - : [ - BoxShadow( - color: AppTheme.leafGreen - .withOpacity(0.3), - blurRadius: 16, - offset: const Offset(0, 6), - ), - ], + BlocBuilder( + builder: (context, state) { + final isLoading = state is AuthLoading; + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + AnimatedContainer( + duration: const Duration(milliseconds: 200), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + boxShadow: isLoading + ? [] + : [ + BoxShadow( + color: AppTheme.leafGreen.withValues(alpha: 0.3), + blurRadius: 16, + offset: const Offset(0, 6), ), - child: ElevatedButton( - onPressed: - isLoading ? null : () => _submit(context), - style: ElevatedButton.styleFrom( - backgroundColor: isLoading - ? AppTheme.mintGreen.withOpacity(0.7) - : AppTheme.leafGreen, - disabledBackgroundColor: - AppTheme.mintGreen.withOpacity(0.6), - padding: - const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), - ), - child: isLoading - ? const SizedBox( - height: 22, - width: 22, - child: CircularProgressIndicator( - strokeWidth: 2.5, - valueColor: AlwaysStoppedAnimation( - Colors.white), - ), - ) - : const Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Icon(Icons.login_rounded, - size: 20, color: Colors.white), - SizedBox(width: 10), - Text( - 'Ingresar de forma segura', - style: TextStyle( - color: Colors.white, - fontSize: 15, - fontWeight: FontWeight.bold), - ), - ], - ), - ), - ), - - // 📍 ENLACE DE REDIRECCIÓN AL REGISTRO CIUDADANO - const SizedBox(height: 20), - TextButton( - onPressed: () { - // 🚀 Forzamos la navegación limpia usando la ruta de texto directo - context.push('/register'); - }, - child: const Text( - '¿No tienes una cuenta ciudadana? Regístrate aquí', - style: TextStyle( - color: AppTheme.leafGreen, - fontWeight: FontWeight.w700, - fontSize: 13, - ), + ], + ), + child: ElevatedButton( + onPressed: isLoading ? null : () => _submit(context), + style: ElevatedButton.styleFrom( + backgroundColor: isLoading + ? AppTheme.mintGreen.withValues(alpha: 0.7) + : AppTheme.leafGreen, + disabledBackgroundColor: + AppTheme.mintGreen.withValues(alpha: 0.6), + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: isLoading + ? const SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator( + strokeWidth: 2.5, + valueColor: AlwaysStoppedAnimation(Colors.white), ), ) - ]); - }, + : const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.login_rounded, + size: 20, color: Colors.white), + SizedBox(width: 10), + Text( + 'Ingresar de forma segura', + style: TextStyle( + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.bold), + ), + ], + ), + ), ), - ], // Fin children de la Column - ), // Fin Column - ), // Fin Form - ), // Fin SingleChildScrollView - ), // Fin SafeArea - ), // Fin BlocListener - ); // Fin Scaffold (Asegúrate de que tenga el punto y coma aquí) + const SizedBox(height: 20), + TextButton( + onPressed: () => context.push('/register'), + child: const Text( + '¿No tienes una cuenta ciudadana? Regístrate aquí', + style: TextStyle( + color: AppTheme.leafGreen, + fontWeight: FontWeight.w700, + fontSize: 13, + ), + ), + ) + ], + ); + }, + ), + ], + ), + ); + } + + Widget _buildDemoHint() { + return const Text( + 'Credenciales de demo: ciudadano@ejemplo.com / password123', + style: TextStyle(fontSize: 12, color: Colors.black54), + ); + } +} diff --git a/lib/features/auth/presentation/screens/register_screen.dart b/lib/features/auth/presentation/screens/register_screen.dart index a49cff7..6b49405 100644 --- a/lib/features/auth/presentation/screens/register_screen.dart +++ b/lib/features/auth/presentation/screens/register_screen.dart @@ -57,7 +57,7 @@ class _RegisterScreenState extends State { // Dropdown para seleccionar la Colonia (Requisito MVP) DropdownButtonFormField( - value: _selectedColonia, + initialValue: _selectedColonia, decoration: const InputDecoration(labelText: 'Selecciona tu Colonia / Domicilio', border: OutlineInputBorder()), items: MockWasteData.schedules.map((zone) { return DropdownMenuItem(value: zone.colonia, child: Text(zone.colonia)); diff --git a/pubspec.lock b/pubspec.lock index c255b1f..dbb28a7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,26 +37,26 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: @@ -65,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dbus: + dependency: transitive + description: + name: dbus + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 + url: "https://pub.dev" + source: hosted + version: "0.7.12" equatable: dependency: "direct main" description: @@ -77,10 +85,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: @@ -176,30 +184,62 @@ packages: url: "https://pub.dev" source: hosted version: "13.2.5" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + latlong2: + dependency: "direct main" + description: + name: latlong2 + sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe" + url: "https://pub.dev" + source: hosted + version: "0.9.1" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -236,26 +276,26 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.19" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.18.0" mgrs_dart: dependency: transitive description: @@ -276,10 +316,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_provider_linux: dependency: transitive description: @@ -425,18 +465,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: @@ -457,10 +497,10 @@ packages: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.11" timezone: dependency: transitive description: @@ -489,10 +529,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -534,5 +574,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.6.0 <4.0.0" + dart: ">=3.10.0-0 <4.0.0" flutter: ">=3.27.0"