import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:real_estate_mobile/core/analytics/firebase_analytics_config.dart'; import 'package:real_estate_mobile/core/analytics/umami_analytics.dart'; import 'package:real_estate_mobile/core/constants/app_colors.dart'; import 'package:real_estate_mobile/core/widgets/app_shell.dart'; import 'package:real_estate_mobile/features/auth/presentation/providers/auth_provider.dart'; import 'package:real_estate_mobile/features/auth/presentation/screens/login_screen.dart'; import 'package:real_estate_mobile/features/auth/presentation/screens/signup_screen.dart'; import 'package:real_estate_mobile/features/agents/presentation/screens/agent_detail_screen.dart'; import 'package:real_estate_mobile/features/agents/presentation/screens/agent_search_screen.dart'; import 'package:real_estate_mobile/features/faq/presentation/screens/faq_screen.dart'; import 'package:real_estate_mobile/features/home/presentation/screens/home_screen.dart'; import 'package:real_estate_mobile/features/messaging/presentation/screens/conversations_screen.dart'; import 'package:real_estate_mobile/features/messaging/presentation/screens/chat_screen.dart'; import 'package:real_estate_mobile/features/contact/presentation/screens/contact_screen.dart'; import 'package:real_estate_mobile/features/about/presentation/screens/about_screen.dart'; import 'package:real_estate_mobile/features/agents/presentation/screens/agent_network_screen.dart'; import 'package:real_estate_mobile/features/profile/presentation/screens/profile_settings_screen.dart'; import 'package:real_estate_mobile/features/notifications/presentation/screens/notifications_screen.dart'; import 'package:real_estate_mobile/features/agents/presentation/screens/agent_edit_profile_screen.dart'; import 'package:real_estate_mobile/features/profile/presentation/screens/payment_success_screen.dart'; import 'package:real_estate_mobile/features/splash/presentation/screens/splash_screen.dart'; import 'package:real_estate_mobile/features/onboarding/presentation/screens/onboarding_screen.dart'; import 'package:real_estate_mobile/features/auth/presentation/screens/verify_2fa_screen.dart'; import 'package:real_estate_mobile/features/support_chat/presentation/screens/support_chat_screen.dart'; import 'package:real_estate_mobile/features/coming_soon/presentation/screens/coming_soon_screen.dart'; final routerProvider = Provider((ref) { final authRefreshNotifier = _AuthRefreshNotifier(); ref.listen(authProvider, (previous, next) { authRefreshNotifier.notify(); }); ref.onDispose(() { authRefreshNotifier.dispose(); }); // Public routes accessible without authentication (matching web middleware) const publicRoutes = ['/home', '/agents/search', '/agents/detail', '/faq', '/contact', '/about']; const authRoutes = ['/login', '/signup', '/verify-2fa']; return GoRouter( initialLocation: '/splash', refreshListenable: authRefreshNotifier, observers: [ UmamiAnalytics.navigationObserver, FirebaseAnalyticsConfig.navigationObserver, ], redirect: (context, state) { final authState = ref.read(authProvider); final isAuthenticated = authState.status == AuthStatus.authenticated; final isLoading = authState.status == AuthStatus.loading; final location = state.matchedLocation; final isAuthRoute = authRoutes.contains(location); final isPublicRoute = publicRoutes.any( (route) => location == route || location.startsWith('$route/'), ); // Never redirect away from splash or onboarding if (location == '/splash' || location == '/onboarding') return null; // While checking auth status, don't redirect if (isLoading) return null; // Public routes and auth routes are always accessible (no auth required) if (isPublicRoute || isAuthRoute) { // Only redirect: if authenticated user is on login/signup, send to home if (isAuthenticated && isAuthRoute) return '/home'; return null; } // Protected routes require authentication if (!isAuthenticated) return '/login'; return null; }, routes: [ // Splash screen (full screen, no shell) GoRoute( path: '/splash', builder: (context, state) => const SplashScreen(), ), // Onboarding (shown only once on first install) GoRoute( path: '/onboarding', builder: (context, state) => const OnboardingScreen(), ), // Auth routes (no shell — full screen) GoRoute( path: '/login', builder: (context, state) => const LoginScreen(), ), GoRoute( path: '/signup', builder: (context, state) => const SignupScreen(), ), GoRoute( path: '/verify-2fa', builder: (context, state) => const Verify2faScreen(), ), // All main app routes wrapped in AppShell (shared header + bottom nav) ShellRoute( builder: (context, state, child) => AppShell(child: child), routes: [ GoRoute( path: '/home', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const _HomeRouteWrapper(), transitionDuration: const Duration(milliseconds: 200), reverseTransitionDuration: const Duration(milliseconds: 150), transitionsBuilder: _fadeTransition, ), ), GoRoute( path: '/agents/search', pageBuilder: (context, state) { final query = state.uri.queryParameters['q']; final typeId = state.uri.queryParameters['type']; final location = state.uri.queryParameters['location']; final category = state.uri.queryParameters['category']; return CustomTransitionPage( key: state.pageKey, child: AgentSearchScreen( initialQuery: query, initialAgentTypeId: typeId, initialLocation: location, initialCategory: category, ), transitionsBuilder: _fadeTransition, ); }, ), // ── Tab routes (fade) ── GoRoute( path: '/messages', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const ConversationsScreen(), transitionDuration: const Duration(milliseconds: 200), reverseTransitionDuration: const Duration(milliseconds: 150), transitionsBuilder: _fadeTransition, ), ), GoRoute( path: '/agent/network', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const AgentNetworkScreen(), transitionDuration: const Duration(milliseconds: 200), reverseTransitionDuration: const Duration(milliseconds: 150), transitionsBuilder: _fadeTransition, ), ), GoRoute( path: '/profile', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const ProfileSettingsScreen(), transitionDuration: const Duration(milliseconds: 200), reverseTransitionDuration: const Duration(milliseconds: 150), transitionsBuilder: _fadeTransition, ), ), GoRoute( path: '/notifications', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const NotificationsScreen(), transitionDuration: const Duration(milliseconds: 200), reverseTransitionDuration: const Duration(milliseconds: 150), transitionsBuilder: _fadeTransition, ), ), // ── Detail/push routes (slide from right) ── GoRoute( path: '/agents/detail/:id', pageBuilder: (context, state) { final id = state.pathParameters['id']!; return CustomTransitionPage( key: state.pageKey, child: AgentDetailScreen(agentId: id), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ); }, ), GoRoute( path: '/messages/chat/:id', pageBuilder: (context, state) { final id = state.pathParameters['id']!; return CustomTransitionPage( key: state.pageKey, child: ChatScreen(conversationId: id), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ); }, ), GoRoute( path: '/coming-soon', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: ComingSoonScreen( title: state.uri.queryParameters['title'] ?? 'Coming Soon', ), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ), ), GoRoute( path: '/faq', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const FaqScreen(), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ), ), GoRoute( path: '/contact', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const ContactScreen(), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ), ), GoRoute( path: '/about', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const AboutScreen(), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ), ), GoRoute( path: '/agent/edit-profile', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const AgentEditProfileScreen(), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ), ), GoRoute( path: '/support/chat', pageBuilder: (context, state) => CustomTransitionPage( key: state.pageKey, child: const SupportChatScreen(), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ), ), GoRoute( path: '/payment-success', pageBuilder: (context, state) { final sessionId = state.uri.queryParameters['session_id']; return CustomTransitionPage( key: state.pageKey, child: PaymentSuccessScreen(sessionId: sessionId), transitionDuration: const Duration(milliseconds: 250), reverseTransitionDuration: const Duration(milliseconds: 200), transitionsBuilder: _slideTransition, ); }, ), ], ), ], ); }); /// Always shows the user HomeScreen for /home. Agents reach their own /// dashboard via the Edit Profile tab in the Profile section. class _HomeRouteWrapper extends ConsumerWidget { const _HomeRouteWrapper(); @override Widget build(BuildContext context, WidgetRef ref) { final authState = ref.watch(authProvider); // Show loader only while auth is actively loading (checking token) if (authState.status == AuthStatus.loading) { return const Center( child: CircularProgressIndicator( color: AppColors.accentOrange, ), ); } // Home icon always shows the User Home Page regardless of role. // Agents access their own dashboard via the Edit Profile tab in /profile. return const HomeScreen(); } } /// Slide from right transition for push-style navigation. /// The entering page slides in from the right while the exiting page /// slides slightly left + fades, giving a native iOS-like depth effect. Widget _slideTransition( BuildContext context, Animation animation, Animation secondaryAnimation, Widget child, ) { // Entering page: slide in from right final slideIn = Tween( begin: const Offset(1, 0), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeOutCubic, )); // Exiting page: slide slightly left + dim final slideOut = Tween( begin: Offset.zero, end: const Offset(-0.25, 0), ).animate(CurvedAnimation( parent: secondaryAnimation, curve: Curves.easeOutCubic, )); final fadeOut = Tween(begin: 1.0, end: 0.9).animate( CurvedAnimation(parent: secondaryAnimation, curve: Curves.easeOut), ); return SlideTransition( position: slideOut, child: FadeTransition( opacity: fadeOut, child: SlideTransition( position: slideIn, child: DecoratedBox( decoration: const BoxDecoration(color: Colors.white), child: child, ), ), ), ); } /// Smooth crossfade transition for tab-to-tab navigation. Widget _fadeTransition( BuildContext context, Animation animation, Animation secondaryAnimation, Widget child, ) { return FadeTransition( opacity: CurvedAnimation(parent: animation, curve: Curves.easeInOut), child: DecoratedBox( decoration: const BoxDecoration(color: Colors.white), child: child, ), ); } /// Notifier that triggers GoRouter refresh when auth state changes. class _AuthRefreshNotifier extends ChangeNotifier { void notify() { notifyListeners(); } }