feat: implement dynamic content loading for home sections with Riverpod and default fallbacks.
This commit is contained in:
@@ -1,23 +1,122 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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 TestimonialsSection extends StatelessWidget {
|
||||
/// Default testimonials — matches web's hardcoded defaultTestimonials fallback.
|
||||
const _defaultTestimonials = [
|
||||
TestimonialItem(
|
||||
rating: 5,
|
||||
title: 'I have been working with Lorem ..',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Sed dignissim, nisl eget tincidunt vulputate, lacus justo bibendum ipsum, vitae tempus risus lorem at nunc. Integer sed arcu vitae risus feugiat vehicula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.',
|
||||
author: 'Conor Kenney',
|
||||
role: 'Chief Operations Officer',
|
||||
),
|
||||
TestimonialItem(
|
||||
rating: 5,
|
||||
title: 'I have been working with Lorem ..',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Sed dignissim, nisl eget tincidunt vulputate, lacus justo bibendum ipsum, vitae tempus risus lorem at nunc. Integer sed arcu vitae risus feugiat vehicula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.',
|
||||
author: 'Conor Kenney',
|
||||
role: 'Chief Operations Officer',
|
||||
),
|
||||
TestimonialItem(
|
||||
rating: 5,
|
||||
title: 'I have been working with Lorem ..',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Sed dignissim, nisl eget tincidunt vulputate, lacus justo bibendum ipsum, vitae tempus risus lorem at nunc. Integer sed arcu vitae risus feugiat vehicula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.',
|
||||
author: 'Conor Kenney',
|
||||
role: 'Chief Operations Officer',
|
||||
),
|
||||
];
|
||||
|
||||
/// Default section 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: _defaultTestimonials,
|
||||
);
|
||||
|
||||
class TestimonialsSection extends ConsumerWidget {
|
||||
const TestimonialsSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final homeState = ref.watch(homeProvider);
|
||||
final cmsTestimonials = homeState.content?.testimonials;
|
||||
|
||||
// Use CMS data if available with non-empty testimonials, otherwise fallback
|
||||
final data =
|
||||
(cmsTestimonials != null && cmsTestimonials.testimonials.isNotEmpty)
|
||||
? cmsTestimonials
|
||||
: _defaultContent;
|
||||
|
||||
final title = data.title.isNotEmpty ? data.title : _defaultContent.title;
|
||||
final subtitle =
|
||||
data.subtitle.isNotEmpty ? data.subtitle : _defaultContent.subtitle;
|
||||
final testimonials =
|
||||
data.testimonials.isNotEmpty ? data.testimonials : _defaultTestimonials;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// Testimonial card
|
||||
// Section header
|
||||
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: 8),
|
||||
Text(
|
||||
subtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Testimonial cards carousel
|
||||
SizedBox(
|
||||
height: 380,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
itemCount: 3,
|
||||
itemCount: testimonials.length,
|
||||
itemBuilder: (context, index) => Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: _buildTestimonialCard(),
|
||||
child: _buildTestimonialCard(testimonials[index]),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -25,7 +124,7 @@ class TestimonialsSection extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTestimonialCard() {
|
||||
Widget _buildTestimonialCard(TestimonialItem testimonial) {
|
||||
return Container(
|
||||
width: 315,
|
||||
padding: const EdgeInsets.all(24),
|
||||
@@ -48,9 +147,9 @@ class TestimonialsSection extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'I have been working with Lorem ..',
|
||||
style: TextStyle(
|
||||
Text(
|
||||
testimonial.title,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -58,10 +157,10 @@ class TestimonialsSection extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Expanded(
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Sed dignissim, nisl eget tincidunt vulputate, lacus justo bibendum ipsum, vitae tempus risus lorem at nunc.',
|
||||
style: TextStyle(
|
||||
testimonial.content,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
@@ -73,7 +172,7 @@ class TestimonialsSection extends StatelessWidget {
|
||||
// Stars
|
||||
Row(
|
||||
children: List.generate(
|
||||
5,
|
||||
testimonial.rating,
|
||||
(index) => const Padding(
|
||||
padding: EdgeInsets.only(right: 4),
|
||||
child: Icon(
|
||||
@@ -86,18 +185,18 @@ class TestimonialsSection extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Author info
|
||||
const Text(
|
||||
'Conor Kenney',
|
||||
style: TextStyle(
|
||||
Text(
|
||||
testimonial.author,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Chief Operations Officer',
|
||||
style: TextStyle(
|
||||
Text(
|
||||
testimonial.role,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
Reference in New Issue
Block a user