feat: Implement initial filtering for agent search from the home screen, enable footer navigation, and add provider stability checks.
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user