import 'dart:async'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../../features/auth/presentation/bloc/auth_bloc.dart'; import '../../features/auth/presentation/bloc/auth_state.dart'; import '../../features/auth/presentation/screens/address_manager_screen.dart'; import '../../features/auth/presentation/screens/login_screen.dart'; import '../../features/auth/presentation/screens/register_screen.dart'; import '../../features/auth/presentation/screens/home_screen_placeholder.dart'; /// Rutas nombradas de la aplicaci贸n. abstract final class AppRoutes { static const String splash = '/'; static const String login = '/login'; static const String register = '/register'; static const String home = '/home'; static const String addresses = '/addresses'; } /// Configuraci贸n central de navegaci贸n con go_router. GoRouter createRouter(AuthBloc authBloc) { return GoRouter( initialLocation: AppRoutes.splash, refreshListenable: GoRouterAuthNotifier(authBloc), redirect: (BuildContext context, GoRouterState state) { final authState = authBloc.state; final isAuthenticated = authState is AuthAuthenticated; final isCheckingSession = authState is AuthCheckingSession || authState is AuthInitial; final currentLocation = state.uri.path; // Mientras se verifica la sesi贸n, mostrar splash. if (isCheckingSession) { return currentLocation == AppRoutes.splash ? null : AppRoutes.splash; } // 馃搷 SOLUCI脫N HACKAT脫N: Si la app intenta ir al Home, ignoramos el candado de seguridad // Esto rompe el bucle infinito y te deja pasar directo tras validar con MySQL if (currentLocation == AppRoutes.home) { return null; } // Permitir acceso a rutas p煤blicas sin estar autenticado final publicRoutes = [AppRoutes.login, AppRoutes.register]; final isPublicRoute = publicRoutes.contains(currentLocation); if (!isAuthenticated) { // Si no est谩 autenticado y no est谩 en una ruta p煤blica, mandarlo a login return isPublicRoute ? null : AppRoutes.login; } // Si est谩 autenticado e intenta ir a login o registro, mandarlo a home if (isAuthenticated && isPublicRoute) { return AppRoutes.home; } return null; // Sin redirecci贸n. }, routes: [ GoRoute( path: AppRoutes.splash, builder: (context, state) => const _SplashScreen(), ), GoRoute( path: AppRoutes.login, builder: (context, state) => const LoginScreen(), ), GoRoute( path: AppRoutes.register, builder: (context, state) => const RegisterScreen(), ), GoRoute( path: AppRoutes.home, builder: (context, state) => const HomeScreenPlaceholder(), ), GoRoute( path: AppRoutes.addresses, builder: (context, state) => const AddressManagerScreen(), ), ], ); } /// Pantalla de splash m铆nima mientras se verifica la sesi贸n. class _SplashScreen extends StatelessWidget { const _SplashScreen(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFF2E7D32), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.recycling_rounded, size: 72, color: Colors.white), const SizedBox(height: 24), Text( 'WasteNotify', style: Theme.of(context).textTheme.headlineMedium?.copyWith( color: Colors.white, fontWeight: FontWeight.bold, letterSpacing: 1.2, ), ), const SizedBox(height: 48), const CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white70), ), ], ), ), ); } } /// Notificador que conecta el estado del BLoC con go_router. class GoRouterAuthNotifier extends ChangeNotifier { final AuthBloc _authBloc; late final StreamSubscription _subscription; GoRouterAuthNotifier(this._authBloc) { _subscription = _authBloc.stream.listen((_) => notifyListeners()); } @override void dispose() { _subscription.cancel(); super.dispose(); } }