import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:real_estate_mobile/core/constants/app_colors.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 content — matches web's hardcoded defaultContent fallback. const _defaultContent = TestimonialsContent( title: "Our Clients' Trust Is Our Foundation", subtitle: 'We help buyers, sellers, and investors close successful real estate deals with confidence in every transaction.', ratingInfo: 'Clients rate our real estate services 4.9 out of 5 on average, based on recent client reviews.', stats: [ StatItem( iconPath: 'cities', boldText: '25+ Cities &', normalText: 'neighborhoods served', ), StatItem( iconPath: 'properties', boldText: '1,000+ Properties', normalText: 'bought and sold for our clients.', ), ], testimonials: [], ); /// Map of stat icon keys to local SVG asset paths and Material fallback icons. const _statIconMap = { 'cities': ('assets/icons/cities_icon.svg', Icons.location_city), 'properties': ('assets/icons/properties_icon.svg', Icons.home_work), }; class TrustStatsSection extends ConsumerWidget { const TrustStatsSection({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final homeState = ref.watch(homeProvider); final cmsTestimonials = homeState.content?.testimonials; // Use CMS data if available, otherwise fallback (same pattern as web) final data = cmsTestimonials ?? _defaultContent; final title = data.title.isNotEmpty ? data.title : _defaultContent.title; final subtitle = data.subtitle.isNotEmpty ? data.subtitle : _defaultContent.subtitle; final ratingInfo = data.ratingInfo.isNotEmpty ? data.ratingInfo : _defaultContent.ratingInfo; final stats = data.stats.isNotEmpty ? data.stats : _defaultContent.stats; // Extract rating number from ratingInfo string (e.g. "4.9" from "...4.9 out of 5...") final ratingValue = _extractRating(ratingInfo); return Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( children: [ Text( title, textAlign: TextAlign.center, style: const TextStyle( fontFamily: 'Fractul', fontSize: 25, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), const SizedBox(height: 16), Text( subtitle, textAlign: TextAlign.center, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.primaryDark, ), ), const SizedBox(height: 8), Text( ratingInfo, textAlign: TextAlign.center, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.primaryDark, ), ), const SizedBox(height: 16), const Divider(color: AppColors.divider), const SizedBox(height: 16), // Stats row Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Dynamic stats from CMS Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: stats .map((stat) => Padding( padding: const EdgeInsets.only(bottom: 12), child: _buildStatItem(stat), )) .toList(), ), ), // Rating visual const SizedBox(width: 16), _buildRatingVisual(ratingValue), ], ), ], ), ); } Widget _buildStatItem(StatItem stat) { final iconEntry = _statIconMap[stat.iconPath]; final svgPath = iconEntry?.$1; final fallbackIcon = iconEntry?.$2 ?? Icons.info_outline; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (svgPath != null) Padding( padding: const EdgeInsets.only(right: 8, top: 2), child: SvgPicture.asset( svgPath, width: 27, height: 27, placeholderBuilder: (_) => Icon( fallbackIcon, size: 27, color: AppColors.accentOrange, ), ), ) else Padding( padding: const EdgeInsets.only(right: 8, top: 2), child: Icon( fallbackIcon, size: 27, color: AppColors.accentOrange, ), ), Expanded( child: RichText( text: TextSpan( children: [ TextSpan( text: '${stat.boldText}\n', style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), TextSpan( text: stat.normalText, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ], ), ), ), ], ); } Widget _buildRatingVisual(double rating) { final fullStars = rating.floor(); final hasHalfStar = (rating - fullStars) >= 0.3; final totalStars = 5; return Column( children: [ Text( rating.toStringAsFixed(1), style: const TextStyle( fontFamily: 'Fractul', fontSize: 32, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), Row( children: List.generate( totalStars, (index) { if (index < fullStars) { return const Icon( Icons.star, color: AppColors.accentOrange, size: 18, ); } else if (index == fullStars && hasHalfStar) { return const Icon( Icons.star_half, color: AppColors.accentOrange, size: 18, ); } else { return const Icon( Icons.star_border, color: AppColors.accentOrange, size: 18, ); } }, ), ), ], ); } /// Extract numeric rating from ratingInfo string. /// e.g. "Clients rate our real estate services 4.9 out of 5..." → 4.9 double _extractRating(String ratingInfo) { final match = RegExp(r'(\d+\.?\d*)\s*out\s*of\s*\d+').firstMatch(ratingInfo); if (match != null) { return double.tryParse(match.group(1)!) ?? 4.9; } return 4.9; } }