From bdb7b04fbf72b091e776c7ef5e645d689c0a9c57 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Sat, 7 Mar 2026 23:28:21 +0530 Subject: [PATCH] feat: Add FAQ screen and navigation, implement authentication-gated access for home screen features, and dynamically render header menu options based on authentication status. --- assets/icons/home_faq_icon.svg | 4 + .../screens/agent_search_screen.dart | 17 +- .../faq/presentation/screens/faq_screen.dart | 545 ++++++++++++++++++ .../presentation/screens/home_screen.dart | 15 +- .../presentation/widgets/home_header.dart | 164 ++++-- lib/routing/app_router.dart | 27 +- 6 files changed, 717 insertions(+), 55 deletions(-) create mode 100644 assets/icons/home_faq_icon.svg create mode 100644 lib/features/faq/presentation/screens/faq_screen.dart diff --git a/assets/icons/home_faq_icon.svg b/assets/icons/home_faq_icon.svg new file mode 100644 index 0000000..b06f6ae --- /dev/null +++ b/assets/icons/home_faq_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/features/agents/presentation/screens/agent_search_screen.dart b/lib/features/agents/presentation/screens/agent_search_screen.dart index 1615c26..0c64d46 100644 --- a/lib/features/agents/presentation/screens/agent_search_screen.dart +++ b/lib/features/agents/presentation/screens/agent_search_screen.dart @@ -8,6 +8,7 @@ import 'package:real_estate_mobile/features/agents/data/models/agent_profile.dar import 'package:real_estate_mobile/features/agents/data/models/agent_type.dart'; import 'package:real_estate_mobile/features/agents/presentation/providers/search_agents_provider.dart'; import 'package:real_estate_mobile/features/agents/presentation/widgets/agent_filter_sheet.dart'; +import 'package:real_estate_mobile/features/auth/presentation/providers/auth_provider.dart'; import 'package:real_estate_mobile/features/home/presentation/widgets/home_header.dart'; class AgentSearchScreen extends ConsumerStatefulWidget { @@ -390,14 +391,26 @@ class _AgentSearchScreenState extends ConsumerState { svgPath: 'assets/icons/nav_messages_icon.svg', fallbackIcon: Icons.chat_bubble, isSelected: false, - onTap: () {}, + onTap: () { + final authState = ref.read(authProvider); + if (authState.status != AuthStatus.authenticated) { + context.push('/login'); + return; + } + }, ), _buildNavItem( index: 3, svgPath: 'assets/icons/nav_profile_icon.svg', fallbackIcon: Icons.person_outline, isSelected: false, - onTap: () {}, + onTap: () { + final authState = ref.read(authProvider); + if (authState.status != AuthStatus.authenticated) { + context.push('/login'); + return; + } + }, ), ], ), diff --git a/lib/features/faq/presentation/screens/faq_screen.dart b/lib/features/faq/presentation/screens/faq_screen.dart new file mode 100644 index 0000000..5e07567 --- /dev/null +++ b/lib/features/faq/presentation/screens/faq_screen.dart @@ -0,0 +1,545 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:real_estate_mobile/core/constants/app_colors.dart'; +import 'package:real_estate_mobile/core/network/api_client.dart'; +import 'package:real_estate_mobile/features/auth/presentation/providers/auth_provider.dart'; +import 'package:real_estate_mobile/features/home/presentation/widgets/home_header.dart'; + +// ── Data models ── + +class FaqItem { + final int id; + final String icon; + final String question; + final String answer; + final String category; + + const FaqItem({ + required this.id, + required this.icon, + required this.question, + required this.answer, + required this.category, + }); + + factory FaqItem.fromJson(Map json) { + return FaqItem( + id: json['id'] as int? ?? 0, + icon: json['icon'] as String? ?? '', + question: json['question'] as String? ?? '', + answer: json['answer'] as String? ?? '', + category: json['category'] as String? ?? '', + ); + } +} + +// ── Default data (same as web) ── + +const _defaultFaqs = [ + FaqItem( + id: 1, + icon: 'assets/icons/home_faq_icon.svg', + question: 'How do I update my listing status?', + answer: + 'To update your listing status, log in to your Agent Dashboard. Locate the property in your "Active Listings" tab, click the three-dot menu on the right, and select "Update Status." You can change it to Pending, Sold, or Temporarily Off Market. Changes are reflected immediately.', + category: 'agent', + ), + FaqItem( + id: 2, + icon: 'assets/icons/home_faq_icon.svg', + question: 'What is the pre-approval process?', + answer: + 'The pre-approval process involves submitting your financial documents to a lender who will review your credit history, income, assets, and debts. Once approved, you\'ll receive a pre-approval letter stating how much you can borrow, which strengthens your position when making offers on properties.', + category: 'buying', + ), + FaqItem( + id: 3, + icon: 'assets/icons/home_faq_icon.svg', + question: 'How do I schedule an open house?', + answer: + 'To schedule an open house, navigate to your Agent Dashboard and select the property you want to showcase. Click on "Schedule Open House" and choose your preferred date and time.', + category: 'agent', + ), + FaqItem( + id: 4, + icon: 'assets/icons/home_faq_icon.svg', + question: 'How do I reset my password?', + answer: + 'To reset your password, click on the "Login" button and then select "Forgot Password." Enter your registered email address and we\'ll send you a password reset link. Follow the link to create a new password.', + category: 'platform', + ), + FaqItem( + id: 5, + icon: 'assets/icons/home_faq_icon.svg', + question: 'How do I contact a listing agent directly?', + answer: + 'You can contact a listing agent by visiting the property page and clicking the "Contact Agent" button. You\'ll be able to send a message directly to the agent through our secure messaging system.', + category: 'buying', + ), + FaqItem( + id: 6, + icon: 'assets/icons/home_faq_icon.svg', + question: 'What if I find inaccurate data on a listing?', + answer: + 'If you find inaccurate information on a listing, please use the "Report Issue" button on the property page. Describe the inaccuracy and our team will review and correct it within 24-48 hours.', + category: 'platform', + ), +]; + +// ── Provider ── + +final faqProvider = + StateNotifierProvider.autoDispose((ref) { + return FaqNotifier(); +}); + +class FaqState { + final List faqs; + final bool isLoading; + final int? expandedId; + + const FaqState({ + this.faqs = const [], + this.isLoading = true, + this.expandedId, + }); + + FaqState copyWith({ + List? faqs, + bool? isLoading, + int? expandedId, + bool clearExpanded = false, + }) { + return FaqState( + faqs: faqs ?? this.faqs, + isLoading: isLoading ?? this.isLoading, + expandedId: clearExpanded ? null : (expandedId ?? this.expandedId), + ); + } +} + +class FaqNotifier extends StateNotifier { + FaqNotifier() : super(const FaqState()) { + _load(); + } + + Future _load() async { + try { + final response = await ApiClient.instance.dio.get('/cms/page/faq'); + final data = response.data['data'] as List?; + if (data != null) { + for (final record in data) { + final map = record as Map; + if (map['sectionKey'] == 'faqContent') { + final content = map['content'] as Map?; + if (content != null) { + final faqsList = content['faqs'] as List?; + if (faqsList != null && faqsList.isNotEmpty) { + final faqs = faqsList + .map((e) => FaqItem.fromJson(e as Map)) + .toList(); + state = state.copyWith( + faqs: faqs, + isLoading: false, + expandedId: faqs.first.id, + ); + return; + } + } + } + } + } + } catch (_) { + // Fall through to defaults + } + + // Use defaults + state = state.copyWith( + faqs: _defaultFaqs, + isLoading: false, + expandedId: _defaultFaqs.first.id, + ); + } + + void toggleFaq(int id) { + if (state.expandedId == id) { + state = state.copyWith(clearExpanded: true); + } else { + state = state.copyWith(expandedId: id); + } + } +} + +// ── Screen ── + +class FaqScreen extends ConsumerWidget { + const FaqScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(faqProvider); + + return Scaffold( + backgroundColor: Colors.white, + body: Column( + children: [ + SafeArea( + bottom: false, + child: const HomeHeader(), + ), + Expanded( + child: state.isLoading + ? const Center( + child: CircularProgressIndicator( + color: AppColors.accentOrange), + ) + : ListView( + padding: const EdgeInsets.fromLTRB(0, 20, 0, 30), + children: [ + // Title + const Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Text( + 'Frequently Asked Questions ?', + style: TextStyle( + fontFamily: 'Fractul', + fontSize: 24, + fontWeight: FontWeight.w700, + color: AppColors.primaryDark, + ), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 24), + + // FAQ items + ...state.faqs.map((faq) => _FaqAccordion( + faq: faq, + isExpanded: state.expandedId == faq.id, + onTap: () => + ref.read(faqProvider.notifier).toggleFaq(faq.id), + )), + + const SizedBox(height: 30), + + // Still Need Help section + const _StillNeedHelpSection(), + ], + ), + ), + ], + ), + bottomNavigationBar: _buildBottomNavBar(context, ref), + ); + } + + Widget _buildBottomNavBar(BuildContext context, WidgetRef ref) { + return Container( + decoration: const BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide(color: Color(0xFFE8E8E8), width: 1), + ), + ), + child: SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildNavItem( + context: context, + svgPath: 'assets/icons/nav_home_icon.svg', + fallbackIcon: Icons.home, + isSelected: false, + onTap: () => context.go('/home'), + ), + _buildNavItem( + context: context, + svgPath: 'assets/icons/nav_list_icon.svg', + fallbackIcon: Icons.list, + isSelected: false, + onTap: () => context.push('/agents/search'), + ), + _buildNavItem( + context: context, + svgPath: 'assets/icons/nav_messages_icon.svg', + fallbackIcon: Icons.chat_bubble, + isSelected: false, + onTap: () { + final authState = ref.read(authProvider); + if (authState.status != AuthStatus.authenticated) { + context.push('/login'); + return; + } + }, + ), + _buildNavItem( + context: context, + svgPath: 'assets/icons/nav_profile_icon.svg', + fallbackIcon: Icons.person_outline, + isSelected: false, + onTap: () { + final authState = ref.read(authProvider); + if (authState.status != AuthStatus.authenticated) { + context.push('/login'); + return; + } + }, + ), + ], + ), + ), + ), + ); + } + + Widget _buildNavItem({ + required BuildContext context, + required String svgPath, + required IconData fallbackIcon, + required bool isSelected, + required VoidCallback onTap, + }) { + return GestureDetector( + onTap: onTap, + behavior: HitTestBehavior.opaque, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: SvgPicture.asset( + svgPath, + width: 24, + height: 24, + colorFilter: isSelected + ? const ColorFilter.mode(AppColors.primaryDark, BlendMode.srcIn) + : const ColorFilter.mode( + AppColors.accentOrange, BlendMode.srcIn), + placeholderBuilder: (_) => Icon( + fallbackIcon, + size: 24, + color: isSelected ? AppColors.primaryDark : AppColors.accentOrange, + ), + ), + ), + ); + } +} + +// ── Accordion item ── + +class _FaqAccordion extends StatelessWidget { + final FaqItem faq; + final bool isExpanded; + final VoidCallback onTap; + + const _FaqAccordion({ + required this.faq, + required this.isExpanded, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 0), + decoration: BoxDecoration( + border: Border.all( + color: AppColors.primaryDark.withValues(alpha: 0.15), + width: 1, + ), + borderRadius: BorderRadius.circular(7), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Question row + GestureDetector( + onTap: onTap, + behavior: HitTestBehavior.opaque, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 18), + child: Row( + children: [ + // Icon + SvgPicture.asset( + 'assets/icons/home_faq_icon.svg', + width: 22, + height: 22, + placeholderBuilder: (_) => const Icon( + Icons.home_outlined, + color: AppColors.accentOrange, + size: 22, + ), + ), + const SizedBox(width: 14), + + // Question text + Expanded( + child: Text( + faq.question, + style: TextStyle( + fontFamily: isExpanded ? 'Fractul' : 'SourceSerif4', + fontSize: 16, + fontWeight: + isExpanded ? FontWeight.w500 : FontWeight.w600, + color: AppColors.primaryDark, + ), + ), + ), + const SizedBox(width: 12), + + // Chevron + Icon( + isExpanded + ? Icons.keyboard_arrow_down + : Icons.keyboard_arrow_right, + color: AppColors.primaryDark, + size: 20, + ), + ], + ), + ), + ), + + // Answer (expanded) + if (isExpanded) ...[ + // Divider + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Divider( + height: 1, + color: AppColors.primaryDark.withValues(alpha: 0.15), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(56, 14, 20, 18), + child: Text( + faq.answer, + style: const TextStyle( + fontFamily: 'SourceSerif4', + fontSize: 14, + fontWeight: FontWeight.w400, + color: AppColors.primaryDark, + height: 1.5, + ), + ), + ), + ], + ], + ), + ); + } +} + +// ── Still Need Help section ── + +class _StillNeedHelpSection extends StatelessWidget { + const _StillNeedHelpSection(); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 0), + decoration: BoxDecoration( + border: Border.all( + color: AppColors.primaryDark.withValues(alpha: 0.15), + width: 1, + ), + borderRadius: BorderRadius.circular(7), + ), + padding: const EdgeInsets.fromLTRB(24, 24, 20, 24), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Left side: text + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Still Need Help ?', + style: TextStyle( + fontFamily: 'Fractul', + fontSize: 14, + fontWeight: FontWeight.w700, + color: AppColors.primaryDark, + ), + ), + const SizedBox(height: 10), + const Text( + 'Our Support Team Is Available\nMon-Fri, 9am-6pm IST', + style: TextStyle( + fontFamily: 'SourceSerif4', + fontSize: 14, + fontWeight: FontWeight.w400, + color: AppColors.primaryDark, + height: 1.4, + ), + ), + ], + ), + ), + + // Right side: buttons + Column( + children: [ + // Start Live Chat + _buildSupportButton( + icon: Icons.chat_bubble_outline, + label: 'Start Live Chat', + onTap: () {}, + ), + const SizedBox(height: 12), + // Email Support + _buildSupportButton( + icon: Icons.email_outlined, + label: 'Email Support', + onTap: () {}, + ), + ], + ), + ], + ), + ); + } + + Widget _buildSupportButton({ + required IconData icon, + required String label, + required VoidCallback onTap, + }) { + return GestureDetector( + onTap: onTap, + child: Container( + width: 162, + height: 38, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(7), + border: Border.all( + color: AppColors.primaryDark.withValues(alpha: 0.15), + width: 1, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: 18, color: AppColors.primaryDark), + const SizedBox(width: 6), + Text( + label, + style: const TextStyle( + fontFamily: 'SourceSerif4', + fontSize: 14, + fontWeight: FontWeight.w400, + color: AppColors.primaryDark, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/home/presentation/screens/home_screen.dart b/lib/features/home/presentation/screens/home_screen.dart index 475f419..87381b8 100644 --- a/lib/features/home/presentation/screens/home_screen.dart +++ b/lib/features/home/presentation/screens/home_screen.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:real_estate_mobile/core/constants/app_colors.dart'; +import 'package:real_estate_mobile/features/auth/presentation/providers/auth_provider.dart'; import 'package:real_estate_mobile/features/home/presentation/widgets/featured_professionals_section.dart'; import 'package:real_estate_mobile/features/home/presentation/widgets/features_section.dart'; import 'package:real_estate_mobile/features/home/presentation/widgets/hero_section.dart'; @@ -10,14 +12,14 @@ import 'package:real_estate_mobile/features/home/presentation/widgets/testimonia import 'package:real_estate_mobile/features/home/presentation/widgets/top_professionals_section.dart'; import 'package:real_estate_mobile/features/home/presentation/widgets/trust_stats_section.dart'; -class HomeScreen extends StatefulWidget { +class HomeScreen extends ConsumerStatefulWidget { const HomeScreen({super.key}); @override - State createState() => _HomeScreenState(); + ConsumerState createState() => _HomeScreenState(); } -class _HomeScreenState extends State { +class _HomeScreenState extends ConsumerState { int _selectedIndex = 0; @override @@ -148,6 +150,13 @@ class _HomeScreenState extends State { context.push('/agents/search'); return; } + if (index == 2 || index == 3) { + final authState = ref.read(authProvider); + if (authState.status != AuthStatus.authenticated) { + context.push('/login'); + return; + } + } setState(() => _selectedIndex = index); }, behavior: HitTestBehavior.opaque, diff --git a/lib/features/home/presentation/widgets/home_header.dart b/lib/features/home/presentation/widgets/home_header.dart index cbf0164..7cfabb4 100644 --- a/lib/features/home/presentation/widgets/home_header.dart +++ b/lib/features/home/presentation/widgets/home_header.dart @@ -154,7 +154,10 @@ class _MenuDrawer extends ConsumerWidget { context, icon: Icons.help_outline, label: "FAQ's", - onTap: () => Navigator.pop(context), + onTap: () { + Navigator.pop(context); + context.push('/faq'); + }, ), _buildMenuItem( context, @@ -165,55 +168,128 @@ class _MenuDrawer extends ConsumerWidget { const SizedBox(height: 16), const Divider(color: AppColors.divider, height: 1), const SizedBox(height: 16), - _buildMenuItem( - context, - icon: Icons.person_outline, - label: 'Account Settings', - onTap: () => Navigator.pop(context), - ), - _buildMenuItem( - context, - icon: Icons.notifications_outlined, - label: 'Notification Settings', - onTap: () => Navigator.pop(context), - ), - const SizedBox(height: 16), - const Divider(color: AppColors.divider, height: 1), - const SizedBox(height: 24), - // Log Out button - SizedBox( - width: double.infinity, - child: OutlinedButton.icon( - onPressed: () { - Navigator.pop(context); - ref.read(authProvider.notifier).logout(); - }, - icon: const Icon( - Icons.logout, - color: AppColors.accentOrange, - size: 20, - ), - label: const Text( - 'Log Out', - style: TextStyle( - fontFamily: 'Fractul', - fontSize: 16, - fontWeight: FontWeight.w700, + if (ref.watch(authProvider).status == + AuthStatus.authenticated) ...[ + _buildMenuItem( + context, + icon: Icons.person_outline, + label: 'Account Settings', + onTap: () => Navigator.pop(context), + ), + _buildMenuItem( + context, + icon: Icons.notifications_outlined, + label: 'Notification Settings', + onTap: () => Navigator.pop(context), + ), + const SizedBox(height: 16), + const Divider(color: AppColors.divider, height: 1), + const SizedBox(height: 24), + // Log Out button + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: () { + Navigator.pop(context); + ref.read(authProvider.notifier).logout(); + }, + icon: const Icon( + Icons.logout, color: AppColors.accentOrange, + size: 20, ), - ), - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 16), - side: const BorderSide( - color: AppColors.accentOrange, - width: 1.5, + label: const Text( + 'Log Out', + style: TextStyle( + fontFamily: 'Fractul', + fontSize: 16, + fontWeight: FontWeight.w700, + color: AppColors.accentOrange, + ), ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), + style: OutlinedButton.styleFrom( + padding: + const EdgeInsets.symmetric(vertical: 16), + side: const BorderSide( + color: AppColors.accentOrange, + width: 1.5, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), ), ), ), - ), + ] else ...[ + // Login button + SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: () { + Navigator.pop(context); + context.push('/login'); + }, + icon: const Icon( + Icons.login, + color: Colors.white, + size: 20, + ), + label: const Text( + 'Log In', + style: TextStyle( + fontFamily: 'Fractul', + fontSize: 16, + fontWeight: FontWeight.w700, + color: Colors.white, + ), + ), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.accentOrange, + padding: + const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + ), + ), + ), + const SizedBox(height: 12), + // Sign Up button + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: () { + Navigator.pop(context); + context.push('/signup'); + }, + icon: const Icon( + Icons.person_add_outlined, + color: AppColors.accentOrange, + size: 20, + ), + label: const Text( + 'Sign Up', + style: TextStyle( + fontFamily: 'Fractul', + fontSize: 16, + fontWeight: FontWeight.w700, + color: AppColors.accentOrange, + ), + ), + style: OutlinedButton.styleFrom( + padding: + const EdgeInsets.symmetric(vertical: 16), + side: const BorderSide( + color: AppColors.accentOrange, + width: 1.5, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + ), + ), + ), + ], ], ), ), diff --git a/lib/routing/app_router.dart b/lib/routing/app_router.dart index 2b12978..2d28ea2 100644 --- a/lib/routing/app_router.dart +++ b/lib/routing/app_router.dart @@ -5,6 +5,7 @@ import 'package:real_estate_mobile/features/auth/presentation/providers/auth_pro 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_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'; final routerProvider = Provider((ref) { @@ -18,25 +19,35 @@ final routerProvider = Provider((ref) { authRefreshNotifier.dispose(); }); + // Public routes accessible without authentication (matching web middleware) + const publicRoutes = ['/home', '/agents/search', '/faq']; + const authRoutes = ['/login', '/signup']; + return GoRouter( - initialLocation: '/login', + initialLocation: '/home', refreshListenable: authRefreshNotifier, redirect: (context, state) { final authState = ref.read(authProvider); final isAuthenticated = authState.status == AuthStatus.authenticated; final isLoading = authState.status == AuthStatus.loading; - final isAuthRoute = state.matchedLocation == '/login' || - state.matchedLocation == '/signup'; + final location = state.matchedLocation; + final isAuthRoute = authRoutes.contains(location); + final isPublicRoute = publicRoutes.any( + (route) => location == route || location.startsWith('$route/'), + ); // While checking auth status, don't redirect if (isLoading) return null; - // If authenticated and on auth page, go to home + // If authenticated and on auth page (login/signup), go to home if (isAuthenticated && isAuthRoute) return '/home'; - // If not authenticated and on protected page, go to login - if (!isAuthenticated && !isAuthRoute) return '/login'; + // Public routes are always accessible + if (isPublicRoute || isAuthRoute) return null; + + // Protected routes require authentication — redirect to login + if (!isAuthenticated) return '/login'; return null; }, @@ -60,6 +71,10 @@ final routerProvider = Provider((ref) { return AgentSearchScreen(initialQuery: query); }, ), + GoRoute( + path: '/faq', + builder: (context, state) => const FaqScreen(), + ), ], ); });