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/widgets/s3_image.dart'; import 'package:real_estate_mobile/features/agents/data/models/agent_profile.dart'; 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/home/presentation/widgets/home_header.dart'; class AgentSearchScreen extends ConsumerStatefulWidget { final String? initialQuery; const AgentSearchScreen({super.key, this.initialQuery}); @override ConsumerState createState() => _AgentSearchScreenState(); } class _AgentSearchScreenState extends ConsumerState { late TextEditingController _searchController; final ScrollController _scrollController = ScrollController(); @override void initState() { super.initState(); _searchController = TextEditingController(text: widget.initialQuery ?? ''); _scrollController.addListener(_onScroll); // Trigger initial search if query provided if (widget.initialQuery != null && widget.initialQuery!.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { ref .read(searchAgentsProvider.notifier) .search(widget.initialQuery!); }); } } @override void dispose() { _searchController.dispose(); _scrollController.dispose(); super.dispose(); } void _onScroll() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200) { ref.read(searchAgentsProvider.notifier).loadMore(); } } @override Widget build(BuildContext context) { final state = ref.watch(searchAgentsProvider); return Scaffold( backgroundColor: Colors.white, body: Column( children: [ // Shared header SafeArea( bottom: false, child: const HomeHeader(), ), _buildSearchBar(), const SizedBox(height: 12), _buildFilterChips(state.agentTypes, state.selectedAgentTypeId), const SizedBox(height: 12), Expanded(child: _buildAgentList(state)), ], ), bottomNavigationBar: _buildBottomNavBar(), ); } Widget _buildSearchBar() { return Padding( padding: const EdgeInsets.fromLTRB(24, 16, 24, 0), child: SizedBox( height: 42, child: Row( children: [ Expanded( child: Container( decoration: BoxDecoration( border: Border.all( color: AppColors.primaryDark, width: 0.1, ), borderRadius: BorderRadius.circular(7), ), child: TextField( controller: _searchController, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.primaryDark, ), decoration: InputDecoration( hintText: 'Search Agents', hintStyle: TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black.withValues(alpha: 0.5), ), border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, contentPadding: const EdgeInsets.symmetric( horizontal: 14, vertical: 10, ), ), onSubmitted: (value) { ref.read(searchAgentsProvider.notifier).search(value); }, ), ), ), const SizedBox(width: 0), GestureDetector( onTap: () { ref .read(searchAgentsProvider.notifier) .search(_searchController.text); }, child: Container( width: 62, height: 42, decoration: BoxDecoration( color: AppColors.accentOrange, border: Border.all( color: AppColors.primaryDark, width: 0.1, ), borderRadius: BorderRadius.circular(7), ), child: const Icon(Icons.search, color: Colors.white, size: 24), ), ), ], ), ), ); } Widget _buildFilterChips( List agentTypes, String? selectedTypeId) { return SizedBox( height: 32, child: Padding( padding: const EdgeInsets.only(left: 24), child: Row( children: [ Expanded( child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: agentTypes.length, separatorBuilder: (_, __) => const SizedBox(width: 4), itemBuilder: (context, index) { final type = agentTypes[index]; final isSelected = type.id == selectedTypeId; return GestureDetector( onTap: () { ref .read(searchAgentsProvider.notifier) .filterByAgentType( isSelected ? null : type.id); }, child: Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 4), decoration: BoxDecoration( color: isSelected ? AppColors.accentOrange : Colors.transparent, borderRadius: BorderRadius.circular(15), border: Border.all( color: AppColors.primaryDark, width: 0.1, ), ), child: Center( child: Text( type.name, style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: isSelected ? Colors.white : AppColors.primaryDark, ), ), ), ), ); }, ), ), const SizedBox(width: 8), Padding( padding: const EdgeInsets.only(right: 24), child: Container( width: 39, height: 27, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), border: Border.all( color: AppColors.primaryDark, width: 0.1, ), ), child: const Icon( Icons.tune, color: AppColors.primaryDark, size: 20, ), ), ), ], ), ), ); } Widget _buildAgentList(SearchAgentsState state) { if (state.isLoading) { return const Center( child: CircularProgressIndicator(color: AppColors.accentOrange), ); } if (state.error != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, color: AppColors.accentOrange, size: 48), const SizedBox(height: 12), Text( state.error!, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, color: AppColors.primaryDark, ), textAlign: TextAlign.center, ), const SizedBox(height: 16), TextButton( onPressed: () => ref.read(searchAgentsProvider.notifier).refresh(), child: const Text( 'Retry', style: TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.accentOrange, ), ), ), ], ), ); } if (state.agents.isEmpty) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.people_outline, color: AppColors.hintText, size: 48), SizedBox(height: 12), Text( 'No agents found', style: TextStyle( fontFamily: 'Fractul', fontSize: 14, color: AppColors.hintText, ), ), ], ), ); } return ListView.builder( controller: _scrollController, padding: const EdgeInsets.symmetric(horizontal: 24), itemCount: state.agents.length + (state.isLoadingMore ? 1 : 0), itemBuilder: (context, index) { if (index == state.agents.length) { return const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Center( child: CircularProgressIndicator(color: AppColors.accentOrange), ), ); } return Padding( padding: const EdgeInsets.only(bottom: 16), child: _AgentCard(agent: state.agents[index]), ); }, ); } // ── Bottom Navigation Bar (shared style matching HomeScreen) ── Widget _buildBottomNavBar() { 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( index: 0, svgPath: 'assets/icons/nav_home_icon.svg', fallbackIcon: Icons.home, isSelected: false, onTap: () => context.go('/home'), ), _buildNavItem( index: 1, svgPath: 'assets/icons/nav_list_icon.svg', fallbackIcon: Icons.list, isSelected: true, onTap: () {}, ), _buildNavItem( index: 2, svgPath: 'assets/icons/nav_messages_icon.svg', fallbackIcon: Icons.chat_bubble, isSelected: false, onTap: () {}, ), _buildNavItem( index: 3, svgPath: 'assets/icons/nav_profile_icon.svg', fallbackIcon: Icons.person_outline, isSelected: false, onTap: () {}, ), ], ), ), ), ); } Widget _buildNavItem({ required int index, 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, ), ), ), ); } } // --- Agent Card Widget matching Figma design --- class _AgentCard extends StatefulWidget { final AgentProfile agent; const _AgentCard({required this.agent}); @override State<_AgentCard> createState() => _AgentCardState(); } class _AgentCardState extends State<_AgentCard> { bool _bioExpanded = false; @override Widget build(BuildContext context) { final agent = widget.agent; final specializations = agent.specializations; final bio = agent.bio ?? ''; final matchPercent = agent.averageRating != null ? '${(agent.averageRating! * 20).round()}%' : null; return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all(color: AppColors.primaryDark.withValues(alpha: 0.1)), boxShadow: [ BoxShadow( color: const Color(0xFFD9D9D9).withValues(alpha: 0.5), offset: const Offset(0, 10), blurRadius: 20, ), ], ), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Cover image with match badge Stack( children: [ S3Image( imageUrl: agent.avatarUrl, width: double.infinity, height: 265, errorWidget: (_) => Container( width: double.infinity, height: 265, color: AppColors.gradientStart, child: const Icon(Icons.person, size: 80, color: AppColors.primaryDark), ), ), if (matchPercent != null) Positioned( top: 0, right: 0, child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), decoration: const BoxDecoration( color: Color(0xFF638559), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(20), topRight: Radius.circular(20), ), ), child: Text( matchPercent, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xFFF0F5FC), ), ), ), ), ], ), // Card content Padding( padding: const EdgeInsets.fromLTRB(34, 16, 20, 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Name row with verified badge Row( children: [ Flexible( child: Text.rich( TextSpan( children: [ TextSpan( text: agent.firstName, style: const TextStyle( fontFamily: 'Fractul', fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), TextSpan( text: ' ${agent.lastName}', style: const TextStyle( fontFamily: 'Fractul', fontSize: 15, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ], ), overflow: TextOverflow.ellipsis, ), ), if (agent.isVerified) ...[ const SizedBox(width: 6), SvgPicture.asset( 'assets/icons/verified_badge_icon.svg', width: 16, height: 16, placeholderBuilder: (_) => const Icon( Icons.verified, color: Color(0xFF5D8B5A), size: 16, ), ), const SizedBox(width: 4), const Text( '(Verified Expert)', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF5D8B5A), ), ), ], ], ), const SizedBox(height: 4), // Headline / title Text( agent.agentType?.name ?? agent.headline ?? 'Professional', style: const TextStyle( fontFamily: 'Fractul', fontSize: 15, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), const SizedBox(height: 10), // Location + Member Since row Row( children: [ if (agent.location.isNotEmpty) ...[ SvgPicture.asset( 'assets/icons/location_pin_icon.svg', width: 14, height: 14, placeholderBuilder: (_) => const Icon( Icons.location_on, color: AppColors.accentOrange, size: 14, ), ), const SizedBox(width: 4), Text( agent.location, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.primaryDark, ), ), const SizedBox(width: 16), ], SvgPicture.asset( 'assets/icons/calendar_icon.svg', width: 14, height: 14, placeholderBuilder: (_) => const Icon( Icons.calendar_today, color: AppColors.accentOrange, size: 14, ), ), const SizedBox(width: 4), Flexible( child: Text( agent.memberSinceText, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.primaryDark, ), overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 12), // Specialization tags if (specializations.isNotEmpty) ...[ Wrap( spacing: 8, runSpacing: 8, children: specializations .take(8) .map((tag) => _buildTag(tag)) .toList(), ), const SizedBox(height: 14), ], // Bio if (bio.isNotEmpty) ...[ Text.rich( TextSpan( children: [ TextSpan( text: _bioExpanded || bio.length <= 150 ? bio : '${bio.substring(0, 150)}... ', style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 10, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), if (bio.length > 150) WidgetSpan( child: GestureDetector( onTap: () => setState(() => _bioExpanded = !_bioExpanded), child: Text( _bioExpanded ? 'Show Less.' : 'Show More.', style: const TextStyle( fontFamily: 'Fractul', fontSize: 10, fontWeight: FontWeight.w600, color: AppColors.primaryDark, decoration: TextDecoration.underline, ), ), ), ), ], ), ), ], ], ), ), ], ), ); } Widget _buildTag(String label) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), border: Border.all(color: AppColors.primaryDark, width: 0.7), ), child: Text( label, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ); } }