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/constants/app_colors.dart'; import 'package:real_estate_mobile/features/agents/data/models/filterable_field.dart'; import 'package:real_estate_mobile/features/agents/presentation/providers/agents_provider.dart'; import 'package:real_estate_mobile/features/agents/presentation/providers/search_agents_provider.dart'; import 'package:real_estate_mobile/features/home/data/models/landing_page_content.dart'; import 'package:real_estate_mobile/features/home/presentation/providers/home_provider.dart'; /// Default hero content — matches web's hardcoded defaultHeroContent fallback. const _defaultHero = HeroContent( headline: 'Discover verified, top-rated real estate professionals.', description: 'Discover verified, top-rated real estate professionals', ctaButtonText: 'See All Agents', helperText: 'Connect with trusted local agents to explore homes, compare options, and make smarter decisions.', ); class HeroSection extends ConsumerStatefulWidget { const HeroSection({super.key}); @override ConsumerState createState() => _HeroSectionState(); } class _HeroSectionState extends ConsumerState { final _searchController = TextEditingController(); String? _selectedTypeId; String _selectedTypeName = 'Types'; String? _selectedLocation; String _selectedLocationName = 'Location'; String? _selectedCategory; String _selectedCategoryName = 'Categories'; /// Cached filter fields fetched on-demand for Location/Categories pickers. List? _cachedFilterFields; @override void dispose() { _searchController.dispose(); super.dispose(); } void _navigateToSearch() { final query = _searchController.text.trim(); final params = {}; if (query.isNotEmpty) params['q'] = Uri.encodeComponent(query); if (_selectedTypeId != null) params['type'] = _selectedTypeId!; if (_selectedLocation != null) params['location'] = Uri.encodeComponent(_selectedLocation!); if (_selectedCategory != null) params['category'] = Uri.encodeComponent(_selectedCategory!); final queryString = params.entries.map((e) => '${e.key}=${e.value}').join('&'); context.push('/agents/search${queryString.isNotEmpty ? '?$queryString' : ''}'); } void _showTypePicker() { final topProState = ref.read(topProfessionalsProvider); final agentTypes = topProState.agentTypes; if (agentTypes.isEmpty) { // If types not loaded yet, just navigate to search context.push('/agents/search'); return; } showModalBottomSheet( context: context, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(15)), ), builder: (ctx) { return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 12), Container( width: 40, height: 4, decoration: BoxDecoration( color: AppColors.divider, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(height: 16), const Text( 'Select Agent Type', style: TextStyle( fontFamily: 'Fractul', fontSize: 18, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), const SizedBox(height: 8), // "All Types" option ListTile( title: const Text( 'All Types', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 16, color: AppColors.primaryDark, ), ), trailing: _selectedTypeId == null ? const Icon(Icons.check, color: AppColors.accentOrange) : null, onTap: () { setState(() { _selectedTypeId = null; _selectedTypeName = 'Types'; }); Navigator.pop(ctx); }, ), ...agentTypes.map((type) => ListTile( title: Text( type.name, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 16, color: AppColors.primaryDark, ), ), trailing: _selectedTypeId == type.id ? const Icon(Icons.check, color: AppColors.accentOrange) : null, onTap: () { setState(() { _selectedTypeId = type.id; _selectedTypeName = type.name; }); Navigator.pop(ctx); }, )), const SizedBox(height: 16), ], ), ); }, ); } @override Widget build(BuildContext context) { final homeState = ref.watch(homeProvider); final hero = homeState.content?.hero ?? _defaultHero; final headline = hero.headline.isNotEmpty ? hero.headline : _defaultHero.headline; final ctaButtonText = hero.ctaButtonText.isNotEmpty ? hero.ctaButtonText : _defaultHero.ctaButtonText; return Stack( children: [ Image.asset( 'assets/images/hero_bg.png', width: double.infinity, height: 760, fit: BoxFit.cover, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: Column( children: [ const SizedBox(height: 42), SizedBox( width: 303, child: Text( headline, textAlign: TextAlign.center, style: const TextStyle( fontFamily: 'Fractul', fontSize: 30, fontWeight: FontWeight.w700, color: AppColors.primaryDark, height: 0.9, ), ), ), const SizedBox(height: 40), _buildSearchCard(ctaButtonText), const SizedBox(height: 32), // "See All Agents" pill button below the card SizedBox( width: 248, height: 50, child: ElevatedButton( onPressed: () => context.push('/agents/search'), style: ElevatedButton.styleFrom( backgroundColor: AppColors.accentOrange, foregroundColor: AppColors.primaryDark, padding: EdgeInsets.zero, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(25), ), ), child: Text( ctaButtonText, style: const TextStyle( fontFamily: 'Fractul', fontSize: 16, fontWeight: FontWeight.w700, ), ), ), ), const SizedBox(height: 32), ], ), ), ], ); } Widget _buildSearchCard(String ctaButtonText) { return Container( padding: const EdgeInsets.fromLTRB(36, 32, 36, 40), decoration: BoxDecoration( color: AppColors.accentOrange, borderRadius: BorderRadius.circular(15), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Center( child: Text( 'Search Agents', style: TextStyle( fontFamily: 'Fractul', fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.primaryDark, ), ), ), const SizedBox(height: 22), // Types — opens picker GestureDetector( onTap: _showTypePicker, child: Container( height: 42, padding: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Expanded( child: Text( _selectedTypeName, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), const Icon( Icons.keyboard_arrow_down, color: AppColors.primaryDark, size: 20, ), ], ), ), ), const SizedBox(height: 18), // Name or Keyword — actual text input Container( height: 42, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), ), child: Theme( data: Theme.of(context).copyWith( inputDecorationTheme: const InputDecorationTheme( filled: false, border: InputBorder.none, ), ), child: TextField( controller: _searchController, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), decoration: const InputDecoration( hintText: 'Name or Keyword', hintStyle: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onSubmitted: (_) => _navigateToSearch(), ), ), ), const SizedBox(height: 18), // Location — opens picker (no dropdown arrow per Figma) GestureDetector( onTap: () => _showFilterPicker( title: 'Select Location', fieldSlug: 'state', currentValue: _selectedLocation, onSelect: (value, label) { setState(() { _selectedLocation = value; _selectedLocationName = label ?? 'Location'; }); }, onClear: () { setState(() { _selectedLocation = null; _selectedLocationName = 'Location'; }); }, ), child: Container( height: 42, padding: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), ), child: Align( alignment: Alignment.centerLeft, child: Text( _selectedLocationName, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ), ), const SizedBox(height: 18), // Categories — opens picker GestureDetector( onTap: () => _showFilterPicker( title: 'Select Category', fieldSlug: 'specialization', currentValue: _selectedCategory, onSelect: (value, label) { setState(() { _selectedCategory = value; _selectedCategoryName = label ?? 'Categories'; }); }, onClear: () { setState(() { _selectedCategory = null; _selectedCategoryName = 'Categories'; }); }, ), child: Container( height: 42, padding: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Expanded( child: Text( _selectedCategoryName, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), const Icon( Icons.keyboard_arrow_down, color: AppColors.primaryDark, size: 20, ), ], ), ), ), const SizedBox(height: 18), // Arrow button — navigates to search with filters SizedBox( width: double.infinity, height: 42, child: ElevatedButton( onPressed: _navigateToSearch, style: ElevatedButton.styleFrom( backgroundColor: AppColors.primaryDark, foregroundColor: Colors.white, padding: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), child: const Icon(Icons.arrow_forward, size: 20), ), ), ], ), ); } Future> _getFilterFields() async { if (_cachedFilterFields != null) return _cachedFilterFields!; // Try reading from the search provider first (if already loaded) final searchState = ref.read(searchAgentsProvider); if (searchState.filterFields.isNotEmpty) { _cachedFilterFields = searchState.filterFields; return _cachedFilterFields!; } // Fetch directly from the repository final repo = ref.read(agentsRepositoryProvider); _cachedFilterFields = await repo.getFilterableFields(); return _cachedFilterFields!; } FilterableField? _findField(List fields, String slug) { for (final f in fields) { if (f.slug == slug || f.slug.toLowerCase().contains(slug.toLowerCase()) || f.name.toLowerCase().contains(slug.toLowerCase())) { return f; } } return null; } Future _showFilterPicker({ required String title, required String fieldSlug, required String? currentValue, required void Function(String value, String? label) onSelect, required VoidCallback onClear, }) async { // Show bottom sheet immediately with a loading state, then populate final filterFields = await _getFilterFields(); if (!mounted) return; final field = _findField(filterFields, fieldSlug); if (field == null || field.options.isEmpty) return; showModalBottomSheet( context: context, backgroundColor: Colors.white, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(15)), ), builder: (ctx) { return DraggableScrollableSheet( initialChildSize: 0.5, minChildSize: 0.3, maxChildSize: 0.8, expand: false, builder: (context, scrollController) { return SafeArea( child: Column( children: [ const SizedBox(height: 12), Container( width: 40, height: 4, decoration: BoxDecoration( color: AppColors.divider, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(height: 16), Text( title, style: const TextStyle( fontFamily: 'Fractul', fontSize: 18, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), const SizedBox(height: 8), Expanded( child: ListView( controller: scrollController, children: [ // "All" option ListTile( title: Text( 'All ${field.name}', style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 16, color: AppColors.primaryDark, ), ), trailing: currentValue == null ? const Icon(Icons.check, color: AppColors.accentOrange) : null, onTap: () { onClear(); Navigator.pop(ctx); }, ), ...field.options.map((option) => ListTile( title: Text( option.label, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 16, color: AppColors.primaryDark, ), ), trailing: currentValue == option.value ? const Icon(Icons.check, color: AppColors.accentOrange) : null, onTap: () { onSelect(option.value, option.label); Navigator.pop(ctx); }, )), ], ), ), ], ), ); }, ); }, ); } }