import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:shimmer/shimmer.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/home/data/models/landing_page_content.dart'; import 'package:real_estate_mobile/features/home/presentation/providers/home_provider.dart'; /// Featured professionals carousel shown above the agents/lenders tab section. /// Uses CMS data from the admin panel (Top Professionals section). class FeaturedProfessionalsSection extends ConsumerStatefulWidget { const FeaturedProfessionalsSection({super.key}); @override ConsumerState createState() => _FeaturedProfessionalsSectionState(); } class _FeaturedProfessionalsSectionState extends ConsumerState { int _currentPage = 0; late ScrollController _scrollController; @override void initState() { super.initState(); _scrollController = ScrollController(); _scrollController.addListener(_onScroll); } @override void dispose() { _scrollController.removeListener(_onScroll); _scrollController.dispose(); super.dispose(); } void _onScroll() { if (!_scrollController.hasClients) return; final cardWidth = MediaQuery.of(context).size.width - 80; if (cardWidth <= 0) return; final page = (_scrollController.offset / cardWidth).round(); if (page != _currentPage) { setState(() => _currentPage = page); } } @override Widget build(BuildContext context) { final homeState = ref.watch(homeProvider); final cmsContent = homeState.content?.topProfessionals; // Use CMS agents list for the featured carousel final professionals = cmsContent?.agents ?? []; if (homeState.isLoading) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: Shimmer.fromColors( baseColor: const Color(0xFFE0E0E0), highlightColor: const Color(0xFFF5F5F5), child: Container( height: 370, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( height: 192, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical( top: Radius.circular(15), ), ), ), Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container(height: 16, width: 160, color: Colors.white), const SizedBox(height: 8), Container(height: 12, width: 100, color: Colors.white), const SizedBox(height: 12), Container(height: 14, width: 180, color: Colors.white), const SizedBox(height: 12), Container(height: 12, width: 140, color: Colors.white), ], ), ), ], ), ), ), ); } if (professionals.isEmpty) { return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: Column( children: [ // Card carousel SizedBox( height: 370, child: ListView.builder( controller: _scrollController, scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), itemCount: professionals.length, itemBuilder: (context, index) { final cardWidth = MediaQuery.of(context).size.width - 80; return SizedBox( width: cardWidth, child: Padding( padding: EdgeInsets.only( left: index == 0 ? 0 : 8, right: index == professionals.length - 1 ? 0 : 8, ), child: _buildFeaturedCard(professionals[index], index), ), ); }, ), ), const SizedBox(height: 16), // Dot indicators _buildDotIndicators(professionals.length), ], ), ); } Widget _buildFeaturedCard(ProfessionalItem item, int index) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15), border: Border.all(color: AppColors.primaryDark, width: 0.1), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Image ClipRRect( borderRadius: const BorderRadius.vertical(top: Radius.circular(15)), child: item.imageUrl.isNotEmpty ? S3Image( imageUrl: item.imageUrl, width: double.infinity, height: 192, alignment: Alignment.topCenter, placeholder: (_) => _buildImagePlaceholder(index), errorWidget: (_) => _buildImagePlaceholder(index), ) : _buildImagePlaceholder(index), ), // Content Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Name Text( item.name, style: const TextStyle( fontFamily: 'Fractul', fontSize: 16, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), // Subtitle if (item.subtitle.isNotEmpty) Text( item.subtitle, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), // Verified badge Row( children: [ SvgPicture.asset( 'assets/icons/verified_badge_icon.svg', width: 16, height: 16, placeholderBuilder: (_) => const Icon( Icons.verified, color: Color(0xFF638559), size: 16, ), ), const SizedBox(width: 4), const Text( 'Verified Agent', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF638559), ), ), ], ), const SizedBox(height: 6), // Location row if (item.location.isNotEmpty) Row( children: [ SvgPicture.asset( 'assets/icons/location_orange_icon.svg', width: 16, height: 16, placeholderBuilder: (_) => const Icon( Icons.location_on, color: AppColors.accentOrange, size: 16, ), ), const SizedBox(width: 6), Expanded( child: Text( item.location, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.primaryDark, ), overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 8), // Experience if (item.experience.isNotEmpty) RichText( text: TextSpan( children: [ const TextSpan( text: 'Experience: ', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), TextSpan( text: item.experience, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ], ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ); } Widget _buildImagePlaceholder(int index) { const colors = [ Color(0xFFC4D9D4), Color(0xFFD4C9B8), Color(0xFFB8C4D4), ]; return Container( width: double.infinity, height: 192, color: colors[index % colors.length], child: const Icon(Icons.person, size: 80, color: AppColors.primaryDark), ); } Widget _buildDotIndicators(int count) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(count, (index) { final isActive = index == _currentPage; return Container( width: isActive ? 10 : 8, height: isActive ? 10 : 8, margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( shape: BoxShape.circle, color: isActive ? AppColors.primaryDark : AppColors.primaryDark.withValues(alpha: 0.3), ), ); }), ); } }