feat: Dynamically fetch and display featured agents from an API endpoint with image resolution and add agent verification status.

This commit is contained in:
pradeepkumar
2026-03-23 11:26:11 +05:30
parent b5c3f6daf9
commit ad2d50138a

View File

@@ -2,6 +2,8 @@ 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/core/network/api_client.dart';
import 'package:real_estate_mobile/core/utils/image_url_resolver.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';
@@ -88,6 +90,7 @@ class _StaticAgent {
final String location;
final String experience;
final String image;
final bool isVerified;
const _StaticAgent({
required this.name,
@@ -96,6 +99,7 @@ class _StaticAgent {
required this.location,
required this.experience,
required this.image,
this.isVerified = true,
});
}
@@ -109,12 +113,15 @@ class FeaturesSection extends ConsumerStatefulWidget {
class _FeaturesSectionState extends ConsumerState<FeaturesSection> {
int _currentPage = 0;
late ScrollController _agentScrollController;
List<_StaticAgent> _agents = List.from(_staticAgents);
bool _agentsLoaded = false;
@override
void initState() {
super.initState();
_agentScrollController = ScrollController();
_agentScrollController.addListener(_onAgentScroll);
_fetchFeaturedAgents();
}
@override
@@ -124,11 +131,57 @@ class _FeaturesSectionState extends ConsumerState<FeaturesSection> {
super.dispose();
}
Future<void> _fetchFeaturedAgents() async {
try {
final dio = ApiClient.instance.dio;
final response = await dio.get('/cms/page/landing');
final sections = response.data['data'] as List<dynamic>?;
if (sections == null) return;
for (final section in sections) {
if (section['sectionKey'] == 'features') {
final content = section['content'] as Map<String, dynamic>?;
final featuredAgents = content?['featuredAgents'] as List<dynamic>?;
if (featuredAgents == null || featuredAgents.isEmpty) return;
final List<_StaticAgent> fetched = [];
for (final agent in featuredAgents) {
String image = agent['imageUrl'] as String? ?? '';
if (image.isNotEmpty && !image.startsWith('http') && !image.startsWith('/')) {
try {
image = await ImageUrlResolver.instance.resolve(image) ?? '';
} catch (_) {}
}
fetched.add(_StaticAgent(
name: agent['name'] as String? ?? '',
role: agent['role'] as String? ?? '',
rating: agent['rating'] as String? ?? '',
location: agent['location'] as String? ?? '',
experience: agent['experience'] as String? ?? '',
image: image,
));
}
if (fetched.isNotEmpty && mounted) {
setState(() {
_agents = fetched;
_agentsLoaded = true;
});
}
break;
}
}
} catch (_) {
// Keep fallback static agents
}
}
void _onAgentScroll() {
if (!_agentScrollController.hasClients) return;
const cardWidth = 254.0 + 16.0; // card width + padding
const cardWidth = 254.0 + 16.0;
final page = (_agentScrollController.offset / cardWidth).round();
if (page != _currentPage && page >= 0 && page < _staticAgents.length) {
if (page != _currentPage && page >= 0 && page < _agents.length) {
setState(() => _currentPage = page);
}
}
@@ -191,10 +244,10 @@ class _FeaturesSectionState extends ConsumerState<FeaturesSection> {
controller: _agentScrollController,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 24),
itemCount: _staticAgents.length,
itemCount: _agents.length,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.only(right: 16),
child: _buildStaticAgentCard(_staticAgents[index]),
child: _buildStaticAgentCard(_agents[index]),
),
),
),
@@ -202,7 +255,7 @@ class _FeaturesSectionState extends ConsumerState<FeaturesSection> {
// Dot indicators
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(_staticAgents.length, (index) {
children: List.generate(_agents.length, (index) {
final isActive = index == _currentPage;
return Container(
width: isActive ? 10 : 8,
@@ -307,7 +360,8 @@ class _FeaturesSectionState extends ConsumerState<FeaturesSection> {
ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(15)),
child: Image.asset(
child: agent.image.startsWith('http')
? Image.network(
agent.image,
width: 254,
height: 161,
@@ -318,6 +372,18 @@ class _FeaturesSectionState extends ConsumerState<FeaturesSection> {
color: const Color(0xFFC4D9D4),
child: const Icon(Icons.person, size: 60, color: AppColors.primaryDark),
),
)
: Image.asset(
(agent.image.isNotEmpty ? agent.image : 'assets/images/agent-anderson.jpg').replaceFirst(RegExp(r'^/'), ''),
width: 254,
height: 161,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Container(
width: 254,
height: 161,
color: const Color(0xFFC4D9D4),
child: const Icon(Icons.person, size: 60, color: AppColors.primaryDark),
),
),
),