feat: implement dynamic content loading for home sections with Riverpod and default fallbacks.

This commit is contained in:
pradeepkumar
2026-02-25 07:48:02 +05:30
parent c9366dedd0
commit 379fbfe0a8
3 changed files with 238 additions and 84 deletions

View File

@@ -1,20 +1,76 @@
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';
class FeaturesSection extends StatelessWidget {
/// Default features content — matches web's hardcoded defaultContent fallback.
const _defaultFeatures = FeaturesContent(
title: 'Find Trusted Real Estate Professionals On Demand',
subtitle:
'Quickly connect with the right agents exactly when you need them.',
features: [
FeatureItem(
iconPath: 'hire_quickly',
title: 'Hire Quickly',
description:
'Connect with experienced local real estate agents who match your needs and preferences. Our simple process helps you get started quickly and move forward without delays or unnecessary complexity.',
),
FeatureItem(
iconPath: 'verified',
title: 'Verified Agents',
description:
'All agents are carefully identity-verified and background-checked to ensure trust and safety. You can confidently work with professionals who meet quality and reliability standards.',
),
FeatureItem(
iconPath: 'top_rated',
title: 'Top Rated',
description:
'Work with highly rated agents known for strong expertise and positive client feedback. Their consistent performance helps you make informed and confident property decisions.',
),
FeatureItem(
iconPath: 'trusted',
title: 'Trusted by Thousands',
description:
'Thousands of buyers, sellers, and renters trust our platform to find reliable agents. Our professionals help people navigate property journeys with confidence and ease.',
),
],
);
/// Map of feature icon keys to local SVG asset paths and Material fallback icons.
const _iconMap = {
'hire_quickly': ('assets/icons/hire_quickly_icon.svg', Icons.bolt),
'verified': ('assets/icons/verified_icon.svg', Icons.verified_user_outlined),
'top_rated': ('assets/icons/top_rated_icon.svg', Icons.star_outline),
'trusted': ('assets/icons/trusted_icon.svg', Icons.people_outline),
};
class FeaturesSection extends ConsumerWidget {
const FeaturesSection({super.key});
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final homeState = ref.watch(homeProvider);
final cmsFeatures = homeState.content?.features;
// Use CMS data if available with non-empty features, otherwise fallback
final data = (cmsFeatures != null && cmsFeatures.features.isNotEmpty)
? cmsFeatures
: _defaultFeatures;
final title = data.title.isNotEmpty ? data.title : _defaultFeatures.title;
final subtitle =
data.subtitle.isNotEmpty ? data.subtitle : _defaultFeatures.subtitle;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
children: [
const Text(
'Find Trusted Real Estate Professionals On Demand',
Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
style: const TextStyle(
fontFamily: 'Fractul',
fontSize: 25,
fontWeight: FontWeight.w700,
@@ -22,10 +78,10 @@ class FeaturesSection extends StatelessWidget {
),
),
const SizedBox(height: 8),
const Text(
'Quickly connect with the right agents exactly when you need them.',
Text(
subtitle,
textAlign: TextAlign.center,
style: TextStyle(
style: const TextStyle(
fontFamily: 'SourceSerif4',
fontSize: 14,
fontWeight: FontWeight.w400,
@@ -33,48 +89,21 @@ class FeaturesSection extends StatelessWidget {
),
),
const SizedBox(height: 24),
_buildFeatureCard(
icon: 'assets/icons/hire_quickly_icon.svg',
fallbackIcon: Icons.bolt,
title: 'Hire Quickly',
description:
'Connect with experienced local real estate agents who match your needs and preferences. Our simple process helps you get started quickly and move forward without delays or unnecessary complexity.',
),
const SizedBox(height: 12),
_buildFeatureCard(
icon: 'assets/icons/verified_icon.svg',
fallbackIcon: Icons.verified_user_outlined,
title: 'Verified Agents',
description:
'All agents are carefully identity-verified and background-checked to ensure trust and safety. You can confidently work with professionals who meet quality and reliability standards.',
),
const SizedBox(height: 12),
_buildFeatureCard(
icon: 'assets/icons/top_rated_icon.svg',
fallbackIcon: Icons.star_outline,
title: 'Top Rated',
description:
'Work with highly rated agents known for strong expertise and positive client feedback. Their consistent performance helps you make informed and confident property decisions.',
),
const SizedBox(height: 12),
_buildFeatureCard(
icon: 'assets/icons/trusted_icon.svg',
fallbackIcon: Icons.people_outline,
title: 'Trusted by Thousands',
description:
'Thousands of buyers, sellers, and renters trust our platform to connect with reliable, verified agents. Our experienced professionals support every step of the property journey, helping users make confident decisions with clarity, trust, and ease.',
),
...data.features.map((feature) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: _buildFeatureCard(feature),
)),
],
),
);
}
Widget _buildFeatureCard({
required String icon,
required IconData fallbackIcon,
required String title,
required String description,
}) {
Widget _buildFeatureCard(FeatureItem feature) {
// Resolve icon: try icon map first, then default
final iconEntry = _iconMap[feature.iconPath];
final svgPath = iconEntry?.$1 ?? 'assets/icons/hire_quickly_icon.svg';
final fallbackIcon = iconEntry?.$2 ?? Icons.bolt;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
@@ -91,7 +120,7 @@ class FeaturesSection extends StatelessWidget {
Row(
children: [
SvgPicture.asset(
icon,
svgPath,
width: 35,
height: 35,
placeholderBuilder: (_) => Icon(
@@ -101,20 +130,22 @@ class FeaturesSection extends StatelessWidget {
),
),
const SizedBox(width: 12),
Text(
title,
style: const TextStyle(
fontFamily: 'Fractul',
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColors.primaryDark,
Expanded(
child: Text(
feature.title,
style: const TextStyle(
fontFamily: 'Fractul',
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColors.primaryDark,
),
),
),
],
),
const SizedBox(height: 12),
Text(
description,
feature.description,
style: const TextStyle(
fontFamily: 'SourceSerif4',
fontSize: 14,