feat: Implement initial filtering for agent search from the home screen, enable footer navigation, and add provider stability checks.

This commit is contained in:
pradeepkumar
2026-03-14 14:53:46 +05:30
parent 6928697c5c
commit ad94db5620
9 changed files with 699 additions and 155 deletions

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.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/agents/data/models/agent_profile.dart';
@@ -96,12 +97,7 @@ class _TopProfessionalsSectionState
// Content
if (topProState.isLoading)
const Padding(
padding: EdgeInsets.symmetric(vertical: 40),
child: CircularProgressIndicator(
color: AppColors.accentOrange,
),
)
_buildShimmerCards()
else if (topProState.error != null)
_buildErrorState(topProState.error!)
else if (activeList.isEmpty)
@@ -164,7 +160,7 @@ class _TopProfessionalsSectionState
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeTab == 'agents'
? AppColors.accentOrange
@@ -172,7 +168,7 @@ class _TopProfessionalsSectionState
borderRadius: BorderRadius.circular(15),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
'assets/icons/agents_tab_icon.svg',
@@ -220,7 +216,7 @@ class _TopProfessionalsSectionState
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeTab == 'lenders'
? AppColors.accentOrange
@@ -228,7 +224,7 @@ class _TopProfessionalsSectionState
borderRadius: BorderRadius.circular(15),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
'assets/icons/lenders_tab_icon.svg',
@@ -270,23 +266,12 @@ class _TopProfessionalsSectionState
);
}
/// Builds the card image from AgentProfile avatar using presigned URL.
Widget _buildCardImage(AgentProfile agent, int index) {
return S3Image(
imageUrl: agent.avatarUrl,
width: double.infinity,
height: 192,
placeholder: (_) => _buildImagePlaceholder(index),
errorWidget: (_) => _buildImagePlaceholder(index),
);
}
Widget _buildImagePlaceholder(int index) {
final assetPath = _fallbackImages[index % _fallbackImages.length];
return Image.asset(
assetPath,
width: double.infinity,
height: 192,
height: double.infinity,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) => _buildAvatarPlaceholder(),
);
@@ -305,15 +290,25 @@ class _TopProfessionalsSectionState
borderRadius: BorderRadius.circular(15),
border: Border.all(color: AppColors.primaryDark, width: 0.1),
),
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Image
ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(15)),
child: _buildCardImage(agent, index),
// Image expands to fill available space above the content
Expanded(
child: ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(15)),
child: S3Image(
imageUrl: agent.avatarUrl,
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
alignment: Alignment.topCenter,
placeholder: (_) => _buildImagePlaceholder(index),
errorWidget: (_) => _buildImagePlaceholder(index),
),
),
),
// Content
@@ -484,6 +479,84 @@ class _TopProfessionalsSectionState
);
}
Widget _buildShimmerCards() {
return SizedBox(
height: 480,
child: Shimmer.fromColors(
baseColor: const Color(0xFFE0E0E0),
highlightColor: const Color(0xFFF5F5F5),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image placeholder
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),
const SizedBox(height: 12),
Row(
children: List.generate(
3,
(_) => Container(
height: 28,
width: 70,
margin: const EdgeInsets.only(right: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
),
),
),
),
const SizedBox(height: 12),
Container(height: 14, width: 200, color: Colors.white),
const SizedBox(height: 12),
Row(
children: List.generate(
5,
(_) => Padding(
padding: const EdgeInsets.only(right: 4),
child: Container(
height: 18,
width: 18,
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
),
),
),
),
],
),
),
],
),
),
),
);
}
Widget _buildStarRating(double rating) {
final fullStars = rating.floor();
final hasHalfStar = (rating - fullStars) >= 0.3;