2026-03-08 12:15:15 +05:30
import ' package:flutter/material.dart ' ;
import ' package:flutter_riverpod/flutter_riverpod.dart ' ;
import ' package:go_router/go_router.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/widgets/s3_image.dart ' ;
2026-03-30 19:20:54 +05:30
import ' package:real_estate_mobile/features/auth/presentation/providers/auth_provider.dart ' ;
2026-03-08 12:15:15 +05:30
// ── Data models (matching web CMS types) ──
class _AboutHero {
final String badge ;
final String headline ;
final String description ;
final String ctaButtonText ;
final String bannerImageUrl ;
final String bannerOverlayText ;
const _AboutHero ( {
this . badge = ' Our Mission ' ,
this . headline = ' Connecting You with Trusted Agents. ' ,
this . description =
' We believe finding the right home shouldn \u2019 t be complicated. Our platform bridges the gap between buyers, sellers, and verified real estate agents for a smooth and transparent experience. ' ,
this . ctaButtonText = ' Find Your Agent ' ,
this . bannerImageUrl = ' ' ,
this . bannerOverlayText = ' Building the future of property. ' ,
} ) ;
factory _AboutHero . fromJson ( Map < String , dynamic > json ) {
return _AboutHero (
badge: json [ ' badge ' ] as String ? ? ? ' Our Mission ' ,
headline: json [ ' headline ' ] as String ? ? ? ' Connecting You with Trusted Agents. ' ,
description: json [ ' description ' ] as String ? ? ?
' We believe finding the right home shouldn \u2019 t be complicated. Our platform bridges the gap between buyers, sellers, and verified real estate agents for a smooth and transparent experience. ' ,
ctaButtonText: json [ ' ctaButtonText ' ] as String ? ? ? ' Find Your Agent ' ,
bannerImageUrl: json [ ' bannerImageUrl ' ] as String ? ? ? ' ' ,
bannerOverlayText: json [ ' bannerOverlayText ' ] as String ? ? ? ' Building the future of property. ' ,
) ;
}
}
class _AboutStatItem {
final String value ;
final String label ;
const _AboutStatItem ( { required this . value , required this . label } ) ;
factory _AboutStatItem . fromJson ( Map < String , dynamic > json ) {
return _AboutStatItem (
value: json [ ' value ' ] as String ? ? ? ' ' ,
label: json [ ' label ' ] as String ? ? ? ' ' ,
) ;
}
}
class _AboutFeatureItem {
final String iconPath ;
final String title ;
final String description ;
const _AboutFeatureItem ( {
required this . iconPath ,
required this . title ,
required this . description ,
} ) ;
factory _AboutFeatureItem . fromJson ( Map < String , dynamic > json ) {
return _AboutFeatureItem (
iconPath: json [ ' iconPath ' ] as String ? ? ? ' ' ,
title: json [ ' title ' ] as String ? ? ? ' ' ,
description: json [ ' description ' ] as String ? ? ? ' ' ,
) ;
}
}
2026-04-15 16:32:31 +05:30
class _AboutTeamCard {
final String iconPath ;
final String title ;
final String description ;
const _AboutTeamCard ( {
required this . iconPath ,
required this . title ,
required this . description ,
2026-03-08 12:15:15 +05:30
} ) ;
2026-04-15 16:32:31 +05:30
factory _AboutTeamCard . fromJson ( Map < String , dynamic > json ) {
return _AboutTeamCard (
iconPath: json [ ' iconPath ' ] as String ? ? ? ' ' ,
title: json [ ' title ' ] as String ? ? ? ' ' ,
description: json [ ' description ' ] as String ? ? ? ' ' ,
2026-03-08 12:15:15 +05:30
) ;
}
}
class _AboutCta {
final String title ;
final String description ;
final String buttonText ;
const _AboutCta ( {
this . title = ' Ready to find a agent ' ,
this . description =
' Search and connect with verified real estate agents who match your needs, location, and goals. ' ,
this . buttonText = ' Find Agents Now ' ,
} ) ;
factory _AboutCta . fromJson ( Map < String , dynamic > json ) {
return _AboutCta (
title: json [ ' title ' ] as String ? ? ? ' Ready to find a agent ' ,
description: json [ ' description ' ] as String ? ? ?
' Search and connect with verified real estate agents who match your needs, location, and goals. ' ,
buttonText: json [ ' buttonText ' ] as String ? ? ? ' Find Agents Now ' ,
) ;
}
}
// ── Defaults ──
2026-04-18 11:34:48 +05:30
// Empty defaults — only CMS-provided content should render (matches web behavior)
const _defaultStats = < _AboutStatItem > [ ] ;
2026-03-08 12:15:15 +05:30
const _defaultFeatures = [
_AboutFeatureItem (
iconPath: ' ' ,
title: ' Verified Agents ' ,
description:
' All agents on our platform are carefully verified to ensure trust, proven expertise, and consistent service, helping users connect with reliable professionals and enjoy a smooth, confident, and transparent property journey ' ,
) ,
_AboutFeatureItem (
iconPath: ' ' ,
title: ' Total Transparency ' ,
description:
' We maintain complete transparency by clearly displaying agent details, reviews, and activity, allowing users to make informed decisions with confidence, clarity, and trust throughout their entire property journey. ' ,
) ,
_AboutFeatureItem (
iconPath: ' ' ,
title: ' Simple Journey ' ,
description:
' Our platform simplifies every step of the property process, from discovering the right agent to closing with confidence, making the experience smooth, fast, and stress-free for users at every stage. ' ,
) ,
] ;
2026-04-15 16:32:31 +05:30
const _defaultTeamCards = < _AboutTeamCard > [ ] ;
2026-03-08 12:15:15 +05:30
// ── State ──
class _AboutState {
final bool isLoading ;
final _AboutHero hero ;
final List < _AboutStatItem > stats ;
final String featuresBadge ;
final List < _AboutFeatureItem > features ;
final String teamTitle ;
final String teamSubtitle ;
2026-04-15 16:32:31 +05:30
final String teamIntro ;
final List < _AboutTeamCard > teamCards ;
2026-03-08 12:15:15 +05:30
final _AboutCta cta ;
final int activeTeamIndex ;
const _AboutState ( {
this . isLoading = true ,
this . hero = const _AboutHero ( ) ,
this . stats = _defaultStats ,
this . featuresBadge = ' Why Choose Us ' ,
this . features = _defaultFeatures ,
this . teamTitle = ' Meet the minds behind the platform ' ,
this . teamSubtitle =
' We are a team of passionate innovators, real estate experts, and designers working together to redefine how people connect with trusted agents. ' ,
2026-04-15 16:32:31 +05:30
this . teamIntro = ' ' ,
this . teamCards = _defaultTeamCards ,
2026-03-08 12:15:15 +05:30
this . cta = const _AboutCta ( ) ,
this . activeTeamIndex = 0 ,
} ) ;
_AboutState copyWith ( {
bool ? isLoading ,
_AboutHero ? hero ,
List < _AboutStatItem > ? stats ,
String ? featuresBadge ,
List < _AboutFeatureItem > ? features ,
String ? teamTitle ,
String ? teamSubtitle ,
2026-04-15 16:32:31 +05:30
String ? teamIntro ,
List < _AboutTeamCard > ? teamCards ,
2026-03-08 12:15:15 +05:30
_AboutCta ? cta ,
int ? activeTeamIndex ,
} ) {
return _AboutState (
isLoading: isLoading ? ? this . isLoading ,
hero: hero ? ? this . hero ,
stats: stats ? ? this . stats ,
featuresBadge: featuresBadge ? ? this . featuresBadge ,
features: features ? ? this . features ,
teamTitle: teamTitle ? ? this . teamTitle ,
teamSubtitle: teamSubtitle ? ? this . teamSubtitle ,
2026-04-15 16:32:31 +05:30
teamIntro: teamIntro ? ? this . teamIntro ,
teamCards: teamCards ? ? this . teamCards ,
2026-03-08 12:15:15 +05:30
cta: cta ? ? this . cta ,
activeTeamIndex: activeTeamIndex ? ? this . activeTeamIndex ,
) ;
}
}
// ── Provider ──
class _AboutNotifier extends StateNotifier < _AboutState > {
_AboutNotifier ( ) : super ( const _AboutState ( ) ) {
2026-04-18 16:35:32 +05:30
// Defer CMS fetch so the first frame paints before the HTTP request
Future . microtask ( _load ) ;
2026-03-08 12:15:15 +05:30
}
Future < void > _load ( ) async {
try {
final response = await ApiClient . instance . dio . get ( ' /cms/page/about ' ) ;
final data = response . data [ ' data ' ] as List < dynamic > ? ;
if ( data ! = null ) {
for ( final record in data ) {
final map = record as Map < String , dynamic > ;
final sectionKey = map [ ' sectionKey ' ] as String ? ;
final content = map [ ' content ' ] as Map < String , dynamic > ? ;
if ( content = = null ) continue ;
switch ( sectionKey ) {
case ' hero ' :
if ( content [ ' headline ' ] ! = null | | content [ ' description ' ] ! = null ) {
state = state . copyWith ( hero: _AboutHero . fromJson ( content ) ) ;
}
case ' stats ' :
final statsList = content [ ' stats ' ] as List < dynamic > ? ;
if ( statsList ! = null & & statsList . isNotEmpty ) {
state = state . copyWith (
stats: statsList
. map ( ( e ) = > _AboutStatItem . fromJson ( e as Map < String , dynamic > ) )
. toList ( ) ,
) ;
}
case ' features ' :
final badge = content [ ' badge ' ] as String ? ;
final featuresList = content [ ' features ' ] as List < dynamic > ? ;
if ( badge ! = null ) {
state = state . copyWith ( featuresBadge: badge ) ;
}
if ( featuresList ! = null & & featuresList . isNotEmpty ) {
state = state . copyWith (
features: featuresList
. map ( ( e ) = > _AboutFeatureItem . fromJson ( e as Map < String , dynamic > ) )
. toList ( ) ,
) ;
}
case ' team ' :
final title = content [ ' title ' ] as String ? ;
final subtitle = content [ ' subtitle ' ] as String ? ;
2026-04-15 16:32:31 +05:30
final intro = content [ ' intro ' ] as String ? ;
final cardsList = content [ ' cards ' ] as List < dynamic > ? ;
2026-03-08 12:15:15 +05:30
if ( title ! = null ) state = state . copyWith ( teamTitle: title ) ;
if ( subtitle ! = null ) state = state . copyWith ( teamSubtitle: subtitle ) ;
2026-04-15 16:32:31 +05:30
if ( intro ! = null ) state = state . copyWith ( teamIntro: intro ) ;
if ( cardsList ! = null & & cardsList . isNotEmpty ) {
2026-03-08 12:15:15 +05:30
state = state . copyWith (
2026-04-15 16:32:31 +05:30
teamCards: cardsList
. map ( ( e ) = > _AboutTeamCard . fromJson ( e as Map < String , dynamic > ) )
2026-03-08 12:15:15 +05:30
. toList ( ) ,
) ;
}
case ' cta ' :
if ( content [ ' title ' ] ! = null | | content [ ' description ' ] ! = null ) {
state = state . copyWith ( cta: _AboutCta . fromJson ( content ) ) ;
}
}
}
}
} catch ( _ ) {
// Fall through to defaults
}
state = state . copyWith ( isLoading: false ) ;
}
void setActiveTeamIndex ( int index ) {
state = state . copyWith ( activeTeamIndex: index ) ;
}
}
final _aboutProvider =
StateNotifierProvider . autoDispose < _AboutNotifier , _AboutState > ( ( ref ) {
return _AboutNotifier ( ) ;
} ) ;
// ── Feature icon mapping (fallback when CMS has no iconPath) ──
const _featureIconFallback = < String , IconData > {
' Verified Agents ' : Icons . verified ,
' Total Transparency ' : Icons . lock_outline ,
' Simple Journey ' : Icons . check_circle_outline ,
} ;
// ── Screen ──
class AboutScreen extends ConsumerWidget {
const AboutScreen ( { super . key } ) ;
@ override
Widget build ( BuildContext context , WidgetRef ref ) {
final state = ref . watch ( _aboutProvider ) ;
2026-03-08 16:05:08 +05:30
if ( state . isLoading ) {
2026-04-18 16:35:32 +05:30
// Full-size placeholder so slide-in transition doesn't pop blank
return Container (
color: Colors . white ,
alignment: Alignment . topCenter ,
padding: const EdgeInsets . only ( top: 120 ) ,
child: const SizedBox (
width: 28 ,
height: 28 ,
child: CircularProgressIndicator (
color: AppColors . accentOrange ,
strokeWidth: 2.5 ,
) ,
2026-03-08 12:15:15 +05:30
) ,
2026-03-08 16:05:08 +05:30
) ;
}
return SingleChildScrollView (
physics: const AlwaysScrollableScrollPhysics ( ) ,
child: Column (
children: [
2026-03-18 17:24:32 +05:30
// Back button
Padding (
padding: const EdgeInsets . fromLTRB ( 16 , 8 , 16 , 0 ) ,
child: Align (
alignment: Alignment . centerLeft ,
child: GestureDetector (
onTap: ( ) {
if ( Navigator . of ( context ) . canPop ( ) ) {
Navigator . of ( context ) . pop ( ) ;
} else {
context . go ( ' /home ' ) ;
}
} ,
child: Container (
width: 36 ,
height: 36 ,
decoration: BoxDecoration (
color: AppColors . primaryDark . withValues ( alpha: 0.08 ) ,
shape: BoxShape . circle ,
) ,
child: const Icon (
Icons . arrow_back_ios_new_rounded ,
color: AppColors . primaryDark ,
size: 18 ,
) ,
) ,
) ,
) ,
) ,
const SizedBox ( height: 10 ) ,
2026-03-08 16:05:08 +05:30
_buildHeroTitle ( state . hero . headline ) ,
const SizedBox ( height: 16 ) ,
_buildHeroSubtitle ( state . hero . description ) ,
const SizedBox ( height: 24 ) ,
_buildBannerImage ( state . hero . bannerImageUrl ) ,
const SizedBox ( height: 10 ) ,
_buildBannerCaption ( state . hero . bannerOverlayText ) ,
const SizedBox ( height: 16 ) ,
_buildStatsRow ( state . stats ) ,
2026-03-14 23:52:45 +05:30
const SizedBox ( height: 24 ) ,
2026-03-08 16:05:08 +05:30
_buildSectionTitle ( state . featuresBadge ) ,
const SizedBox ( height: 12 ) ,
_buildWhyChooseUsSubtitle ( ) ,
const SizedBox ( height: 24 ) ,
. . . state . features . map ( ( f ) = > Padding (
padding: const EdgeInsets . only ( bottom: 15 ) ,
child: _buildFeatureCard ( f ) ,
) ) ,
2026-03-14 23:52:45 +05:30
const SizedBox ( height: 20 ) ,
2026-03-08 16:05:08 +05:30
_buildTeamSection ( context , ref , state ) ,
2026-03-14 23:52:45 +05:30
const SizedBox ( height: 24 ) ,
2026-03-08 16:05:08 +05:30
_buildCtaSection ( context , state . cta ) ,
2026-03-14 23:52:45 +05:30
const SizedBox ( height: 24 ) ,
2026-03-08 16:05:08 +05:30
] ,
2026-03-08 12:15:15 +05:30
) ,
) ;
}
// -- Hero Title --
Widget _buildHeroTitle ( String text ) {
return Padding (
padding: const EdgeInsets . symmetric ( horizontal: 40 ) ,
child: Text (
text ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 25 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
) ,
) ;
}
// -- Hero Subtitle --
Widget _buildHeroSubtitle ( String text ) {
return Padding (
padding: const EdgeInsets . symmetric ( horizontal: 40 ) ,
child: Text (
text ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' SourceSerif4 ' ,
fontSize: 14 ,
fontWeight: FontWeight . w500 ,
color: AppColors . primaryDark ,
) ,
) ,
) ;
}
// -- Banner Image (from CMS or fallback to local asset) --
Widget _buildBannerImage ( String imageUrl ) {
return Padding (
padding: const EdgeInsets . symmetric ( horizontal: 57 ) ,
child: ClipRRect (
borderRadius: BorderRadius . circular ( 15 ) ,
child: SizedBox (
height: 295 ,
width: double . infinity ,
child: imageUrl . isNotEmpty
? S3Image (
imageUrl: imageUrl ,
height: 295 ,
fit: BoxFit . cover ,
errorWidget: ( context ) = > Image . asset (
' assets/images/about_cityscape.png ' ,
height: 295 ,
width: double . infinity ,
fit: BoxFit . cover ,
) ,
)
: Image . asset (
' assets/images/about_cityscape.png ' ,
height: 295 ,
width: double . infinity ,
fit: BoxFit . cover ,
) ,
) ,
) ,
) ;
}
// -- Banner Caption --
Widget _buildBannerCaption ( String text ) {
return Center (
child: Text (
text ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 14 ,
fontWeight: FontWeight . w500 ,
color: AppColors . primaryDark ,
) ,
) ,
) ;
}
// -- Stats Row (dynamic) --
Widget _buildStatsRow ( List < _AboutStatItem > stats ) {
if ( stats . isEmpty ) return const SizedBox . shrink ( ) ;
return Padding (
padding: const EdgeInsets . symmetric ( horizontal: 40 ) ,
child: Row (
mainAxisAlignment: MainAxisAlignment . spaceAround ,
children: stats . map ( ( s ) = > _buildStatItem ( s . value , s . label ) ) . toList ( ) ,
) ,
) ;
}
Widget _buildStatItem ( String value , String label ) {
return Column (
children: [
Text (
value ,
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 14 ,
fontWeight: FontWeight . w500 ,
color: AppColors . accentOrange ,
) ,
) ,
const SizedBox ( height: 2 ) ,
Text (
label ,
style: const TextStyle (
fontFamily: ' SourceSerif4 ' ,
fontSize: 14 ,
fontWeight: FontWeight . w400 ,
color: AppColors . primaryDark ,
) ,
) ,
] ,
) ;
}
// -- Section Title --
Widget _buildSectionTitle ( String text ) {
return Center (
child: Text (
text ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 25 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
) ,
) ;
}
// -- Why Choose Us Subtitle --
Widget _buildWhyChooseUsSubtitle ( ) {
return const Padding (
padding: EdgeInsets . symmetric ( horizontal: 65 ) ,
child: Text (
' Our core principles guide every interaction, ensuring you feel confident and supported at every step. ' ,
textAlign: TextAlign . center ,
style: TextStyle (
fontFamily: ' SourceSerif4 ' ,
fontSize: 14 ,
fontWeight: FontWeight . w400 ,
color: AppColors . primaryDark ,
) ,
) ,
) ;
}
// -- Feature Card (dynamic with S3 icon support) --
Widget _buildFeatureCard ( _AboutFeatureItem feature ) {
final fallbackIcon = _featureIconFallback [ feature . title ] ? ? Icons . star_outline ;
return Padding (
padding: const EdgeInsets . symmetric ( horizontal: 57 ) ,
child: Container (
padding: const EdgeInsets . symmetric ( horizontal: 24 , vertical: 24 ) ,
decoration: BoxDecoration (
borderRadius: BorderRadius . circular ( 15 ) ,
border: Border . all (
color: AppColors . primaryDark . withValues ( alpha: 0.1 ) ,
width: 0.1 ,
) ,
) ,
child: Column (
children: [
if ( feature . iconPath . isNotEmpty )
SizedBox (
width: 36 ,
height: 36 ,
child: S3Image (
imageUrl: feature . iconPath ,
width: 36 ,
height: 36 ,
fit: BoxFit . contain ,
errorWidget: ( _ ) = > Icon (
fallbackIcon ,
size: 36 ,
color: AppColors . accentOrange ,
) ,
) ,
)
else
Icon (
fallbackIcon ,
size: 36 ,
color: AppColors . accentOrange ,
) ,
const SizedBox ( height: 10 ) ,
Text (
feature . title ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 14 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
) ,
const SizedBox ( height: 8 ) ,
Text (
feature . description ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' SourceSerif4 ' ,
fontSize: 14 ,
fontWeight: FontWeight . w400 ,
color: AppColors . primaryDark ,
height: 1.5 ,
) ,
) ,
] ,
) ,
) ,
) ;
}
2026-04-15 16:32:31 +05:30
// -- Team Section (dynamic — matches web: title, subtitle, intro, icon cards) --
2026-03-08 12:15:15 +05:30
Widget _buildTeamSection (
BuildContext context ,
WidgetRef ref ,
_AboutState state ,
) {
2026-04-15 16:32:31 +05:30
final cards = state . teamCards ;
final hasContent = state . teamTitle . isNotEmpty | |
state . teamSubtitle . isNotEmpty | |
state . teamIntro . isNotEmpty | |
cards . isNotEmpty ;
if ( ! hasContent ) return const SizedBox . shrink ( ) ;
2026-03-08 12:15:15 +05:30
return Column (
children: [
2026-04-15 16:32:31 +05:30
if ( state . teamTitle . isNotEmpty )
Padding (
padding: const EdgeInsets . symmetric ( horizontal: 57 ) ,
child: Text (
state . teamTitle ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 25 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
2026-03-08 12:15:15 +05:30
) ,
) ,
2026-04-15 16:32:31 +05:30
if ( state . teamSubtitle . isNotEmpty ) . . . [
const SizedBox ( height: 16 ) ,
Padding (
padding: const EdgeInsets . symmetric ( horizontal: 57 ) ,
child: Text (
state . teamSubtitle ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' SourceSerif4 ' ,
fontSize: 14 ,
fontWeight: FontWeight . w400 ,
color: AppColors . primaryDark ,
) ,
2026-03-08 12:15:15 +05:30
) ,
) ,
2026-04-15 16:32:31 +05:30
] ,
if ( state . teamIntro . isNotEmpty ) . . . [
const SizedBox ( height: 16 ) ,
Padding (
padding: const EdgeInsets . symmetric ( horizontal: 24 ) ,
child: Text (
state . teamIntro ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' SourceSerif4 ' ,
fontSize: 14 ,
fontWeight: FontWeight . w400 ,
color: AppColors . primaryDark ,
height: 1.5 ,
) ,
2026-03-08 12:15:15 +05:30
) ,
) ,
2026-04-15 16:32:31 +05:30
] ,
if ( cards . isNotEmpty ) . . . [
const SizedBox ( height: 24 ) ,
. . . cards . map (
( card ) = > Padding (
padding: const EdgeInsets . fromLTRB ( 24 , 0 , 24 , 15 ) ,
child: _buildTeamCard ( card ) ,
2026-03-08 12:15:15 +05:30
) ,
) ,
2026-04-15 16:32:31 +05:30
] ,
2026-03-08 12:15:15 +05:30
] ,
) ;
}
2026-04-15 16:32:31 +05:30
Widget _buildTeamCard ( _AboutTeamCard card ) {
return Container (
width: double . infinity ,
padding: const EdgeInsets . all ( 24 ) ,
decoration: BoxDecoration (
borderRadius: BorderRadius . circular ( 15 ) ,
border: Border . all (
color: AppColors . primaryDark . withValues ( alpha: 0.1 ) ,
width: 1 ,
) ,
) ,
child: Column (
children: [
if ( card . iconPath . isNotEmpty )
SizedBox (
width: 36 ,
height: 36 ,
child: card . iconPath . startsWith ( ' http ' )
? Image . network (
card . iconPath ,
width: 36 ,
height: 36 ,
fit: BoxFit . contain ,
errorBuilder: ( _ , __ , ___ ) = > const SizedBox . shrink ( ) ,
)
: S3Image (
imageUrl: card . iconPath ,
width: 36 ,
height: 36 ,
fit: BoxFit . contain ,
placeholder: ( _ ) = > const SizedBox . shrink ( ) ,
errorWidget: ( _ ) = > const SizedBox . shrink ( ) ,
) ,
) ,
if ( card . title . isNotEmpty ) . . . [
const SizedBox ( height: 12 ) ,
Text (
card . title ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 16 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
) ,
] ,
if ( card . description . isNotEmpty ) . . . [
const SizedBox ( height: 8 ) ,
Text (
card . description ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' SourceSerif4 ' ,
fontSize: 14 ,
fontWeight: FontWeight . w400 ,
color: AppColors . primaryDark ,
height: 1.5 ,
) ,
) ,
] ,
] ,
) ,
2026-03-08 12:15:15 +05:30
) ;
}
// -- CTA Section (dynamic) --
Widget _buildCtaSection ( BuildContext context , _AboutCta cta ) {
return Column (
children: [
Text (
cta . title ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 25 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
) ,
const SizedBox ( height: 16 ) ,
Padding (
padding: const EdgeInsets . symmetric ( horizontal: 83 ) ,
child: Text (
cta . description ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: ' SourceSerif4 ' ,
fontSize: 14 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
) ,
) ,
const SizedBox ( height: 24 ) ,
// Illustration
Padding (
padding: const EdgeInsets . symmetric ( horizontal: 34 ) ,
child: ClipRRect (
borderRadius: BorderRadius . circular ( 56 ) ,
child: Image . asset (
' assets/images/about_find_agent.jpg ' ,
height: 241 ,
width: double . infinity ,
fit: BoxFit . cover ,
) ,
) ,
) ,
const SizedBox ( height: 24 ) ,
2026-03-30 19:20:54 +05:30
// Find Agents Now button — navigate based on user role
Consumer ( builder: ( context , ref , _ ) {
return GestureDetector (
onTap: ( ) {
final authState = ref . read ( authProvider ) ;
if ( authState . status = = AuthStatus . authenticated & &
authState . user ? . role = = ' AGENT ' ) {
context . go ( ' /home ' ) ;
} else {
context . push ( ' /agents/search ' ) ;
}
} ,
2026-03-08 12:15:15 +05:30
child: Container (
height: 50 ,
width: 210 ,
decoration: BoxDecoration (
color: AppColors . accentOrange ,
borderRadius: BorderRadius . circular ( 25 ) ,
) ,
alignment: Alignment . center ,
child: Text (
2026-03-31 19:36:30 +05:30
( ref . read ( authProvider ) . user ? . role = = ' AGENT ' )
? ' Go to Dashboard '
: cta . buttonText ,
2026-03-08 12:15:15 +05:30
style: const TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 16 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
) ,
) ,
2026-03-30 19:20:54 +05:30
) ;
} ) ,
2026-03-08 12:15:15 +05:30
const SizedBox ( height: 12 ) ,
// Get Help button
GestureDetector (
onTap: ( ) = > context . push ( ' /contact ' ) ,
child: Container (
height: 50 ,
width: 210 ,
decoration: BoxDecoration (
borderRadius: BorderRadius . circular ( 25 ) ,
border: Border . all (
color: AppColors . primaryDark ,
width: 1 ,
) ,
) ,
alignment: Alignment . center ,
child: const Text (
' Get Help ' ,
style: TextStyle (
fontFamily: ' Fractul ' ,
fontSize: 16 ,
fontWeight: FontWeight . w700 ,
color: AppColors . primaryDark ,
) ,
) ,
) ,
) ,
] ,
) ;
}
}