feat: redirect agents to profile on login, update UI layouts, and add analytics dependencies.

This commit is contained in:
pradeepkumar
2026-05-06 13:19:31 +05:30
parent c6b96e19dc
commit 8845e77274
10 changed files with 293 additions and 175 deletions

View File

@@ -9,9 +9,13 @@ PODS:
- GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0)
- PromisesObjC (~> 2.4) - PromisesObjC (~> 2.4)
- connectivity_plus (0.0.1):
- Flutter
- CwlCatchException (2.2.1): - CwlCatchException (2.2.1):
- CwlCatchExceptionSupport (~> 2.2.1) - CwlCatchExceptionSupport (~> 2.2.1)
- CwlCatchExceptionSupport (2.2.1) - CwlCatchExceptionSupport (2.2.1)
- device_info_plus (0.0.1):
- Flutter
- emoji_picker_flutter (0.0.1): - emoji_picker_flutter (0.0.1):
- Flutter - Flutter
- FBAEMKit (18.0.2): - FBAEMKit (18.0.2):
@@ -22,11 +26,20 @@ PODS:
- FBSDKCoreKit_Basics (18.0.2) - FBSDKCoreKit_Basics (18.0.2)
- FBSDKLoginKit (18.0.2): - FBSDKLoginKit (18.0.2):
- FBSDKCoreKit (= 18.0.2) - FBSDKCoreKit (= 18.0.2)
- Firebase/Analytics (11.15.0):
- Firebase/Core
- Firebase/Core (11.15.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 11.15.0)
- Firebase/CoreOnly (11.15.0): - Firebase/CoreOnly (11.15.0):
- FirebaseCore (~> 11.15.0) - FirebaseCore (~> 11.15.0)
- Firebase/Messaging (11.15.0): - Firebase/Messaging (11.15.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseMessaging (~> 11.15.0) - FirebaseMessaging (~> 11.15.0)
- firebase_analytics (11.6.0):
- Firebase/Analytics (= 11.15.0)
- firebase_core
- Flutter
- firebase_core (3.15.2): - firebase_core (3.15.2):
- Firebase/CoreOnly (= 11.15.0) - Firebase/CoreOnly (= 11.15.0)
- Flutter - Flutter
@@ -34,6 +47,24 @@ PODS:
- Firebase/Messaging (= 11.15.0) - Firebase/Messaging (= 11.15.0)
- firebase_core - firebase_core
- Flutter - Flutter
- FirebaseAnalytics (11.15.0):
- FirebaseAnalytics/Default (= 11.15.0)
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseAnalytics/Default (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleAppMeasurement/Default (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseCore (11.15.0): - FirebaseCore (11.15.0):
- FirebaseCoreInternal (~> 11.15.0) - FirebaseCoreInternal (~> 11.15.0)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
@@ -68,6 +99,32 @@ PODS:
- FlutterMacOS - FlutterMacOS
- GoogleSignIn (~> 8.0) - GoogleSignIn (~> 8.0)
- GTMSessionFetcher (>= 3.4.0) - GTMSessionFetcher (>= 3.4.0)
- GoogleAdsOnDeviceConversion (2.1.0):
- GoogleUtilities/Logger (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Core (11.15.0):
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/Default (11.15.0):
- GoogleAdsOnDeviceConversion (= 2.1.0)
- GoogleAppMeasurement/Core (= 11.15.0)
- GoogleAppMeasurement/IdentitySupport (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleAppMeasurement/IdentitySupport (11.15.0):
- GoogleAppMeasurement/Core (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- GoogleDataTransport (10.1.0): - GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4) - PromisesObjC (~> 2.4)
@@ -86,6 +143,9 @@ PODS:
- GoogleUtilities/Logger (8.1.0): - GoogleUtilities/Logger (8.1.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GoogleUtilities/MethodSwizzler (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/Network (8.1.0): - GoogleUtilities/Network (8.1.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib" - "GoogleUtilities/NSData+zlib"
@@ -110,6 +170,8 @@ PODS:
- GTMSessionFetcher/Core - GTMSessionFetcher/Core
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- memory_info (0.0.1):
- Flutter
- nanopb (3.30910.0): - nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0) - nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0) - nanopb/encode (= 3.30910.0)
@@ -138,7 +200,10 @@ PODS:
- FlutterMacOS - FlutterMacOS
DEPENDENCIES: DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`) - emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`)
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
@@ -147,6 +212,7 @@ DEPENDENCIES:
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- memory_info (from `.symlinks/plugins/memory_info/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
@@ -166,10 +232,13 @@ SPEC REPOS:
- FBSDKCoreKit_Basics - FBSDKCoreKit_Basics
- FBSDKLoginKit - FBSDKLoginKit
- Firebase - Firebase
- FirebaseAnalytics
- FirebaseCore - FirebaseCore
- FirebaseCoreInternal - FirebaseCoreInternal
- FirebaseInstallations - FirebaseInstallations
- FirebaseMessaging - FirebaseMessaging
- GoogleAdsOnDeviceConversion
- GoogleAppMeasurement
- GoogleDataTransport - GoogleDataTransport
- GoogleSignIn - GoogleSignIn
- GoogleUtilities - GoogleUtilities
@@ -179,8 +248,14 @@ SPEC REPOS:
- PromisesObjC - PromisesObjC
EXTERNAL SOURCES: EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
emoji_picker_flutter: emoji_picker_flutter:
:path: ".symlinks/plugins/emoji_picker_flutter/ios" :path: ".symlinks/plugins/emoji_picker_flutter/ios"
firebase_analytics:
:path: ".symlinks/plugins/firebase_analytics/ios"
firebase_core: firebase_core:
:path: ".symlinks/plugins/firebase_core/ios" :path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging: firebase_messaging:
@@ -197,6 +272,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/google_sign_in_ios/darwin" :path: ".symlinks/plugins/google_sign_in_ios/darwin"
image_picker_ios: image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios" :path: ".symlinks/plugins/image_picker_ios/ios"
memory_info:
:path: ".symlinks/plugins/memory_info/ios"
package_info_plus: package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios" :path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation: path_provider_foundation:
@@ -215,16 +292,20 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
CwlCatchException: 7acc161b299a6de7f0a46a6ed741eae2c8b4d75a CwlCatchException: 7acc161b299a6de7f0a46a6ed741eae2c8b4d75a
CwlCatchExceptionSupport: 54ccab8d8c78907b57f99717fb19d4cc3bce02dc CwlCatchExceptionSupport: 54ccab8d8c78907b57f99717fb19d4cc3bce02dc
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
emoji_picker_flutter: 8e50ec5caac456a23a78637e02c6293ea0ac8771 emoji_picker_flutter: 8e50ec5caac456a23a78637e02c6293ea0ac8771
FBAEMKit: 56a4f5a9607957b547b6458b17a98c5bb2736367 FBAEMKit: 56a4f5a9607957b547b6458b17a98c5bb2736367
FBSDKCoreKit: 54f1240567e16862ab71c53c33dc347db5c524c5 FBSDKCoreKit: 54f1240567e16862ab71c53c33dc347db5c524c5
FBSDKCoreKit_Basics: eb530cd6a79030dbb11df460aafb2767bcad9028 FBSDKCoreKit_Basics: eb530cd6a79030dbb11df460aafb2767bcad9028
FBSDKLoginKit: 6a70798793e499335a98fab1a758d7da34af196e FBSDKLoginKit: 6a70798793e499335a98fab1a758d7da34af196e
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
firebase_analytics: bf93e20703c95030404d6ddbb1adf05bf5c3885b
firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c
firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7 firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7
FirebaseAnalytics: 6433dfd311ba78084fc93bdfc145e8cb75740eae
FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4 FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843 FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
@@ -234,12 +315,15 @@ SPEC CHECKSUMS:
flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017 google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017
GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64
GoogleAppMeasurement: 700dce7541804bec33db590a5c496b663fbe2539
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4 GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
memory_info: 18a37c81d0c57151f6661fd007b05f8894427a0a
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46

View File

@@ -1336,8 +1336,10 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
color: const Color(0xFFE6E6E6), color: const Color(0xFFE6E6E6),
padding: const EdgeInsets.symmetric(vertical: 24), padding: const EdgeInsets.symmetric(vertical: 24),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const Text('Specialization', const Text('Specialization',
textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontFamily: 'Fractul', fontFamily: 'Fractul',
fontSize: 20, fontSize: 20,
@@ -1345,6 +1347,7 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
color: AppColors.primaryDark)), color: AppColors.primaryDark)),
const SizedBox(height: 4), const SizedBox(height: 4),
const Text('Area Of Expertise and Focus', const Text('Area Of Expertise and Focus',
textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontFamily: 'Fractul', fontFamily: 'Fractul',
fontSize: 14, fontSize: 14,
@@ -1365,8 +1368,7 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
final hasMore = card.values.length > 3; final hasMore = card.values.length > 3;
return Container( return Container(
margin: const EdgeInsets.symmetric(horizontal: 66, vertical: 8), margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
width: 298,
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20), padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
@@ -1375,11 +1377,13 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
const Icon(Icons.category_outlined, const Icon(Icons.category_outlined,
size: 28, color: AppColors.accentOrange), size: 28, color: AppColors.accentOrange),
const SizedBox(height: 10), const SizedBox(height: 10),
Text(displayName, Text(displayName,
textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontFamily: 'Fractul', fontFamily: 'Fractul',
fontSize: 14, fontSize: 14,

View File

@@ -170,7 +170,7 @@ class _AgentSearchScreenState extends ConsumerState<AgentSearchScreen> {
color: AppColors.primaryDark, color: AppColors.primaryDark,
), ),
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Search Agents', hintText: 'Search Professionals',
hintStyle: TextStyle( hintStyle: TextStyle(
fontFamily: 'Fractul', fontFamily: 'Fractul',
fontSize: 14, fontSize: 14,

View File

@@ -65,7 +65,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
// Navigate on auth state changes // Navigate on auth state changes
ref.listen<AuthState>(authProvider, (previous, next) { ref.listen<AuthState>(authProvider, (previous, next) {
if (next.status == AuthStatus.authenticated) { if (next.status == AuthStatus.authenticated) {
context.go('/home'); // Agents land on /profile (defaults to "Edit Profile" tab —
// their dashboard/home view). Users land on /home.
final isAgent = next.user?.role == 'AGENT';
context.go(isAgent ? '/profile' : '/home');
} }
// Navigate to 2FA verification screen // Navigate to 2FA verification screen
if (next.requiresTwoFactor && next.tempToken != null) { if (next.requiresTwoFactor && next.tempToken != null) {

View File

@@ -95,10 +95,12 @@ class _Verify2faScreenState extends ConsumerState<Verify2faScreen> {
final authState = ref.watch(authProvider); final authState = ref.watch(authProvider);
final isLoading = authState.status == AuthStatus.loading; final isLoading = authState.status == AuthStatus.loading;
// Navigate to home when authentication succeeds, reset submit lock on error // Navigate when authentication succeeds, reset submit lock on error.
// Agents land on /profile (Edit Profile tab — their home/dashboard view).
ref.listen<AuthState>(authProvider, (previous, next) { ref.listen<AuthState>(authProvider, (previous, next) {
if (next.status == AuthStatus.authenticated) { if (next.status == AuthStatus.authenticated) {
context.go('/home'); final isAgent = next.user?.role == 'AGENT';
context.go(isAgent ? '/profile' : '/home');
} }
if (next.status != AuthStatus.loading) { if (next.status != AuthStatus.loading) {
_isSubmitting = false; _isSubmitting = false;

View File

@@ -479,25 +479,33 @@ class _FeaturesSectionState extends ConsumerState<FeaturesSection> {
const SizedBox(height: 8), const SizedBox(height: 8),
// Location // Location
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SvgPicture.asset( Padding(
'assets/icons/location_filled_icon.svg', padding: const EdgeInsets.only(top: 2),
width: 15, child: SvgPicture.asset(
height: 15, 'assets/icons/location_filled_icon.svg',
placeholderBuilder: (_) => const Icon( width: 15,
Icons.location_on, height: 15,
color: AppColors.accentOrange, placeholderBuilder: (_) => const Icon(
size: 15, Icons.location_on,
color: AppColors.accentOrange,
size: 15,
),
), ),
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Expanded(
agent.location, child: Text(
style: const TextStyle( agent.location,
fontFamily: 'SourceSerif4', maxLines: 2,
fontSize: 14, overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.w500, style: const TextStyle(
color: AppColors.primaryDark, fontFamily: 'SourceSerif4',
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.primaryDark,
),
), ),
), ),
], ],

View File

@@ -21,18 +21,18 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
iconAsset: 'assets/images/onboarding_1_icon.svg', iconAsset: 'assets/images/onboarding_1_icon.svg',
iconWidth: 110, iconWidth: 110,
iconHeight: 100, iconHeight: 100,
title: 'Connect with Top\nProfessionals', title: 'Connect with Top Professionals',
subtitle: subtitle:
'Professionals can list properties and find qualified\nbuyers faster than ever.', 'Professionals can list properties and find qualified buyers faster than ever.',
), ),
_OnboardingData( _OnboardingData(
bgAsset: 'assets/images/onboarding_2_bg.svg', bgAsset: 'assets/images/onboarding_2_bg.svg',
iconAsset: 'assets/images/onboarding_2_icon.svg', iconAsset: 'assets/images/onboarding_2_icon.svg',
iconWidth: 83, iconWidth: 83,
iconHeight: 75, iconHeight: 75,
title: 'Real help. Real professionals.\nInstantly.', title: 'Real help. Real professionals. Instantly.',
subtitle: subtitle:
'Chat with professionals and get answers about\nproperties instantly.', 'Chat with professionals and get answers about properties instantly.',
), ),
]; ];
@@ -208,150 +208,151 @@ class _OnboardingPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( // Scale the illustration to ~38% of screen height so it shrinks on small
children: [ // devices, leaving room for the title + subtitle without overflow.
// Illustration section - takes upper portion final screenHeight = MediaQuery.of(context).size.height;
Expanded( final illustrationHeight = (screenHeight * 0.38).clamp(220.0, 346.0);
flex: 5,
child: Padding( return SingleChildScrollView(
padding: const EdgeInsets.only(left: 0, top: 8), physics: const ClampingScrollPhysics(),
child: Align( child: Column(
alignment: Alignment.centerLeft, children: [
child: SizedBox( // Illustration section
width: MediaQuery.of(context).size.width - 24, Padding(
child: Stack( padding: const EdgeInsets.only(top: 8),
alignment: Alignment.center, child: SizedBox(
children: [ width: MediaQuery.of(context).size.width - 24,
// Background oval/pill shape with orange gradient height: illustrationHeight,
SvgPicture.asset( child: Stack(
alignment: Alignment.center,
children: [
// Background oval/pill shape with orange gradient
Positioned.fill(
child: SvgPicture.asset(
data.bgAsset, data.bgAsset,
width: 382,
height: 346,
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
// Blurred decoration - bottom right ),
Positioned( // Blurred decoration - bottom right
right: 20, Positioned(
bottom: 20, right: 20,
child: Container( bottom: 20,
width: 160, child: Container(
height: 160, width: 120,
decoration: BoxDecoration( height: 120,
shape: BoxShape.circle, decoration: BoxDecoration(
color: AppColors.accentOrange.withValues(alpha: 0.2), shape: BoxShape.circle,
boxShadow: [ color: AppColors.accentOrange.withValues(alpha: 0.2),
BoxShadow( boxShadow: [
color: AppColors.accentOrange BoxShadow(
.withValues(alpha: 0.15), color: AppColors.accentOrange
blurRadius: 40, .withValues(alpha: 0.15),
spreadRadius: 0, blurRadius: 40,
), spreadRadius: 0,
], ),
), ],
), ),
), ),
// Blurred decoration - top left ),
Positioned( // Blurred decoration - top left
left: 0, Positioned(
top: 0, left: 0,
child: Container( top: 0,
width: 160, child: Container(
height: 160, width: 120,
decoration: BoxDecoration( height: 120,
shape: BoxShape.circle, decoration: BoxDecoration(
color: AppColors.accentOrange.withValues(alpha: 0.1), shape: BoxShape.circle,
boxShadow: [ color: AppColors.accentOrange.withValues(alpha: 0.1),
BoxShadow( boxShadow: [
color: AppColors.accentOrange BoxShadow(
.withValues(alpha: 0.08), color: AppColors.accentOrange
blurRadius: 40, .withValues(alpha: 0.08),
spreadRadius: 0, blurRadius: 40,
), spreadRadius: 0,
], ),
), ],
), ),
), ),
// Main icon + small icons ),
Column( // Main icon + small icons
mainAxisAlignment: MainAxisAlignment.center, Column(
children: [ mainAxisSize: MainAxisSize.min,
SvgPicture.asset( mainAxisAlignment: MainAxisAlignment.center,
data.iconAsset, children: [
width: data.iconWidth, SvgPicture.asset(
height: data.iconHeight, data.iconAsset,
), width: data.iconWidth,
const SizedBox(height: 16), height: data.iconHeight,
// Small property icons row ),
Row( const SizedBox(height: 12),
mainAxisAlignment: MainAxisAlignment.center, // Small property icons row
children: [ Row(
SvgPicture.asset( mainAxisAlignment: MainAxisAlignment.center,
'assets/images/onboarding_small_icon_1.svg', children: [
width: 27, SvgPicture.asset(
height: 27, 'assets/images/onboarding_small_icon_1.svg',
), width: 27,
const SizedBox(width: 8), height: 27,
SvgPicture.asset( ),
'assets/images/onboarding_small_icon_2.svg', const SizedBox(width: 8),
width: 31.5, SvgPicture.asset(
height: 31.5, 'assets/images/onboarding_small_icon_2.svg',
), width: 31.5,
const SizedBox(width: 8), height: 31.5,
SvgPicture.asset( ),
'assets/images/onboarding_small_icon_3.svg', const SizedBox(width: 8),
width: 24, SvgPicture.asset(
height: 27, 'assets/images/onboarding_small_icon_3.svg',
), width: 24,
], height: 27,
), ),
], ],
), ),
], ],
), ),
],
), ),
), ),
), ),
),
// Text content section // Text content section
Expanded( Padding(
flex: 3, padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
const SizedBox(height: 8),
// Title // Title
Text( Text(
data.title, data.title,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontFamily: 'Fractul', fontFamily: 'Fractul',
fontSize: 32, fontSize: 26,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: Color(0xFF0F172A), color: Color(0xFF0F172A),
height: 1.25, height: 1.25,
letterSpacing: -0.8, letterSpacing: -0.8,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 12),
// Subtitle // Subtitle
Text( Text(
data.subtitle, data.subtitle,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontFamily: 'SourceSerif4', fontFamily: 'SourceSerif4',
fontSize: 16, fontSize: 15,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Color(0xFF475569), color: Color(0xFF475569),
height: 1.625, height: 1.5,
), ),
), ),
], ],
), ),
), ),
), ],
], ),
); );
} }
} }

View File

@@ -467,47 +467,54 @@ class _ProfileSettingsTabState extends ConsumerState<ProfileSettingsTab> {
} }
return AlertDialog( return AlertDialog(
scrollable: true,
title: const Text('Change Email Address'), title: const Text('Change Email Address'),
content: SingleChildScrollView( content: Column(
child: Column( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch,
crossAxisAlignment: CrossAxisAlignment.stretch, children: [
children: [ const Text(
const Text( 'A verification link will be sent to your new email. Your email changes only after you click the link.',
'A verification link will be sent to your new email. Your email changes only after you click the link.', style: TextStyle(fontSize: 13),
style: TextStyle(fontSize: 13), ),
const SizedBox(height: 16),
TextField(
controller: newEmailCtl,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
enableSuggestions: false,
textCapitalization: TextCapitalization.none,
autofillHints: const [AutofillHints.email],
decoration: const InputDecoration(
labelText: 'New Email',
hintText: 'new@example.com',
), ),
const SizedBox(height: 16), ),
TextField( const SizedBox(height: 12),
controller: newEmailCtl, TextField(
keyboardType: TextInputType.emailAddress, controller: passwordCtl,
autocorrect: false, obscureText: true,
textCapitalization: TextCapitalization.none, autocorrect: false,
decoration: const InputDecoration( enableSuggestions: false,
labelText: 'New Email', // Empty autofillHints prevents iOS from showing the
hintText: 'new@example.com', // "Strong Password" suggestion bar, which was adding
), // extra bottom inset and shifting the dialog upward.
autofillHints: const <String>[],
decoration: const InputDecoration(
labelText: 'Current Password',
), ),
),
if (localError != null) ...[
const SizedBox(height: 12), const SizedBox(height: 12),
TextField( Text(
controller: passwordCtl, localError!,
obscureText: true, style: const TextStyle(
decoration: const InputDecoration( color: Colors.red,
labelText: 'Current Password', fontSize: 12,
), ),
), ),
if (localError != null) ...[
const SizedBox(height: 12),
Text(
localError!,
style: const TextStyle(
color: Colors.red,
fontSize: 12,
),
),
],
], ],
), ],
), ),
actions: [ actions: [
TextButton( TextButton(

View File

@@ -1,18 +1,20 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:real_estate_mobile/core/constants/app_colors.dart'; import 'package:real_estate_mobile/core/constants/app_colors.dart';
import 'package:real_estate_mobile/features/auth/presentation/providers/auth_provider.dart';
class SplashScreen extends StatefulWidget { class SplashScreen extends ConsumerStatefulWidget {
const SplashScreen({super.key}); const SplashScreen({super.key});
@override @override
State<SplashScreen> createState() => _SplashScreenState(); ConsumerState<SplashScreen> createState() => _SplashScreenState();
} }
class _SplashScreenState extends State<SplashScreen> class _SplashScreenState extends ConsumerState<SplashScreen>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late final AnimationController _controller; late final AnimationController _controller;
late final Animation<double> _fadeAnimation; late final Animation<double> _fadeAnimation;
@@ -43,11 +45,15 @@ class _SplashScreenState extends State<SplashScreen>
final onboardingCompleted = prefs.getBool('onboarding_completed') ?? false; final onboardingCompleted = prefs.getBool('onboarding_completed') ?? false;
if (!mounted) return; if (!mounted) return;
if (onboardingCompleted) { if (!onboardingCompleted) {
context.go('/home');
} else {
context.go('/onboarding'); context.go('/onboarding');
return;
} }
// Agents land on /profile (Edit Profile tab); everyone else on /home.
final authState = ref.read(authProvider);
final isAgent = authState.status == AuthStatus.authenticated &&
authState.user?.role == 'AGENT';
context.go(isAgent ? '/profile' : '/home');
} }
@override @override

View File

@@ -23,6 +23,7 @@ import 'package:real_estate_mobile/features/agents/presentation/screens/agent_ed
import 'package:real_estate_mobile/features/profile/presentation/screens/payment_success_screen.dart'; import 'package:real_estate_mobile/features/profile/presentation/screens/payment_success_screen.dart';
import 'package:real_estate_mobile/features/splash/presentation/screens/splash_screen.dart'; import 'package:real_estate_mobile/features/splash/presentation/screens/splash_screen.dart';
import 'package:real_estate_mobile/features/onboarding/presentation/screens/onboarding_screen.dart'; import 'package:real_estate_mobile/features/onboarding/presentation/screens/onboarding_screen.dart';
import 'package:real_estate_mobile/features/agents/presentation/screens/agent_home_screen.dart';
import 'package:real_estate_mobile/features/auth/presentation/screens/verify_2fa_screen.dart'; import 'package:real_estate_mobile/features/auth/presentation/screens/verify_2fa_screen.dart';
import 'package:real_estate_mobile/features/support_chat/presentation/screens/support_chat_screen.dart'; import 'package:real_estate_mobile/features/support_chat/presentation/screens/support_chat_screen.dart';
import 'package:real_estate_mobile/features/coming_soon/presentation/screens/coming_soon_screen.dart'; import 'package:real_estate_mobile/features/coming_soon/presentation/screens/coming_soon_screen.dart';
@@ -288,8 +289,8 @@ final routerProvider = Provider<GoRouter>((ref) {
); );
}); });
/// Always shows the user HomeScreen for /home. Agents reach their own /// Reactively switches between HomeScreen and AgentHomeScreen based on auth.
/// dashboard via the Edit Profile tab in the Profile section. /// Agents see their dashboard (profile view); users see the discovery home.
class _HomeRouteWrapper extends ConsumerWidget { class _HomeRouteWrapper extends ConsumerWidget {
const _HomeRouteWrapper(); const _HomeRouteWrapper();
@@ -306,8 +307,10 @@ class _HomeRouteWrapper extends ConsumerWidget {
); );
} }
// Home icon always shows the User Home Page regardless of role. if (authState.status == AuthStatus.authenticated &&
// Agents access their own dashboard via the Edit Profile tab in /profile. authState.user?.role == 'AGENT') {
return const AgentHomeScreen();
}
return const HomeScreen(); return const HomeScreen();
} }
} }