feat: redirect agents to profile on login, update UI layouts, and add analytics dependencies.
This commit is contained in:
@@ -9,9 +9,13 @@ PODS:
|
||||
- GoogleUtilities/Environment (~> 8.0)
|
||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||
- PromisesObjC (~> 2.4)
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- CwlCatchException (2.2.1):
|
||||
- CwlCatchExceptionSupport (~> 2.2.1)
|
||||
- CwlCatchExceptionSupport (2.2.1)
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- emoji_picker_flutter (0.0.1):
|
||||
- Flutter
|
||||
- FBAEMKit (18.0.2):
|
||||
@@ -22,11 +26,20 @@ PODS:
|
||||
- FBSDKCoreKit_Basics (18.0.2)
|
||||
- FBSDKLoginKit (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):
|
||||
- FirebaseCore (~> 11.15.0)
|
||||
- Firebase/Messaging (11.15.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseMessaging (~> 11.15.0)
|
||||
- firebase_analytics (11.6.0):
|
||||
- Firebase/Analytics (= 11.15.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- firebase_core (3.15.2):
|
||||
- Firebase/CoreOnly (= 11.15.0)
|
||||
- Flutter
|
||||
@@ -34,6 +47,24 @@ PODS:
|
||||
- Firebase/Messaging (= 11.15.0)
|
||||
- firebase_core
|
||||
- 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):
|
||||
- FirebaseCoreInternal (~> 11.15.0)
|
||||
- GoogleUtilities/Environment (~> 8.1)
|
||||
@@ -68,6 +99,32 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- GoogleSignIn (~> 8.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):
|
||||
- nanopb (~> 3.30910.0)
|
||||
- PromisesObjC (~> 2.4)
|
||||
@@ -86,6 +143,9 @@ PODS:
|
||||
- GoogleUtilities/Logger (8.1.0):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/MethodSwizzler (8.1.0):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Network (8.1.0):
|
||||
- GoogleUtilities/Logger
|
||||
- "GoogleUtilities/NSData+zlib"
|
||||
@@ -110,6 +170,8 @@ PODS:
|
||||
- GTMSessionFetcher/Core
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- memory_info (0.0.1):
|
||||
- Flutter
|
||||
- nanopb (3.30910.0):
|
||||
- nanopb/decode (= 3.30910.0)
|
||||
- nanopb/encode (= 3.30910.0)
|
||||
@@ -138,7 +200,10 @@ PODS:
|
||||
- FlutterMacOS
|
||||
|
||||
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`)
|
||||
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
@@ -147,6 +212,7 @@ DEPENDENCIES:
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`)
|
||||
- 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`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
@@ -166,10 +232,13 @@ SPEC REPOS:
|
||||
- FBSDKCoreKit_Basics
|
||||
- FBSDKLoginKit
|
||||
- Firebase
|
||||
- FirebaseAnalytics
|
||||
- FirebaseCore
|
||||
- FirebaseCoreInternal
|
||||
- FirebaseInstallations
|
||||
- FirebaseMessaging
|
||||
- GoogleAdsOnDeviceConversion
|
||||
- GoogleAppMeasurement
|
||||
- GoogleDataTransport
|
||||
- GoogleSignIn
|
||||
- GoogleUtilities
|
||||
@@ -179,8 +248,14 @@ SPEC REPOS:
|
||||
- PromisesObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
emoji_picker_flutter:
|
||||
:path: ".symlinks/plugins/emoji_picker_flutter/ios"
|
||||
firebase_analytics:
|
||||
:path: ".symlinks/plugins/firebase_analytics/ios"
|
||||
firebase_core:
|
||||
:path: ".symlinks/plugins/firebase_core/ios"
|
||||
firebase_messaging:
|
||||
@@ -197,6 +272,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/google_sign_in_ios/darwin"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
memory_info:
|
||||
:path: ".symlinks/plugins/memory_info/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
@@ -215,16 +292,20 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
|
||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
||||
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
||||
CwlCatchException: 7acc161b299a6de7f0a46a6ed741eae2c8b4d75a
|
||||
CwlCatchExceptionSupport: 54ccab8d8c78907b57f99717fb19d4cc3bce02dc
|
||||
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
||||
emoji_picker_flutter: 8e50ec5caac456a23a78637e02c6293ea0ac8771
|
||||
FBAEMKit: 56a4f5a9607957b547b6458b17a98c5bb2736367
|
||||
FBSDKCoreKit: 54f1240567e16862ab71c53c33dc347db5c524c5
|
||||
FBSDKCoreKit_Basics: eb530cd6a79030dbb11df460aafb2767bcad9028
|
||||
FBSDKLoginKit: 6a70798793e499335a98fab1a758d7da34af196e
|
||||
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
|
||||
firebase_analytics: bf93e20703c95030404d6ddbb1adf05bf5c3885b
|
||||
firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c
|
||||
firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7
|
||||
FirebaseAnalytics: 6433dfd311ba78084fc93bdfc145e8cb75740eae
|
||||
FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
|
||||
FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
|
||||
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
|
||||
@@ -234,12 +315,15 @@ SPEC CHECKSUMS:
|
||||
flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9
|
||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||
google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017
|
||||
GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64
|
||||
GoogleAppMeasurement: 700dce7541804bec33db590a5c496b663fbe2539
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4
|
||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
|
||||
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
||||
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
|
||||
memory_info: 18a37c81d0c57151f6661fd007b05f8894427a0a
|
||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
|
||||
@@ -1336,8 +1336,10 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
|
||||
color: const Color(0xFFE6E6E6),
|
||||
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text('Specialization',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 20,
|
||||
@@ -1345,6 +1347,7 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
|
||||
color: AppColors.primaryDark)),
|
||||
const SizedBox(height: 4),
|
||||
const Text('Area Of Expertise and Focus',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
@@ -1365,8 +1368,7 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
|
||||
final hasMore = card.values.length > 3;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 66, vertical: 8),
|
||||
width: 298,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
@@ -1375,11 +1377,13 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.category_outlined,
|
||||
size: 28, color: AppColors.accentOrange),
|
||||
const SizedBox(height: 10),
|
||||
Text(displayName,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
|
||||
@@ -170,7 +170,7 @@ class _AgentSearchScreenState extends ConsumerState<AgentSearchScreen> {
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search Agents',
|
||||
hintText: 'Search Professionals',
|
||||
hintStyle: TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
|
||||
@@ -65,7 +65,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||
// Navigate on auth state changes
|
||||
ref.listen<AuthState>(authProvider, (previous, next) {
|
||||
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
|
||||
if (next.requiresTwoFactor && next.tempToken != null) {
|
||||
|
||||
@@ -95,10 +95,12 @@ class _Verify2faScreenState extends ConsumerState<Verify2faScreen> {
|
||||
final authState = ref.watch(authProvider);
|
||||
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) {
|
||||
if (next.status == AuthStatus.authenticated) {
|
||||
context.go('/home');
|
||||
final isAgent = next.user?.role == 'AGENT';
|
||||
context.go(isAgent ? '/profile' : '/home');
|
||||
}
|
||||
if (next.status != AuthStatus.loading) {
|
||||
_isSubmitting = false;
|
||||
|
||||
@@ -479,25 +479,33 @@ class _FeaturesSectionState extends ConsumerState<FeaturesSection> {
|
||||
const SizedBox(height: 8),
|
||||
// Location
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/icons/location_filled_icon.svg',
|
||||
width: 15,
|
||||
height: 15,
|
||||
placeholderBuilder: (_) => const Icon(
|
||||
Icons.location_on,
|
||||
color: AppColors.accentOrange,
|
||||
size: 15,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2),
|
||||
child: SvgPicture.asset(
|
||||
'assets/icons/location_filled_icon.svg',
|
||||
width: 15,
|
||||
height: 15,
|
||||
placeholderBuilder: (_) => const Icon(
|
||||
Icons.location_on,
|
||||
color: AppColors.accentOrange,
|
||||
size: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
agent.location,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.primaryDark,
|
||||
Expanded(
|
||||
child: Text(
|
||||
agent.location,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -21,18 +21,18 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
|
||||
iconAsset: 'assets/images/onboarding_1_icon.svg',
|
||||
iconWidth: 110,
|
||||
iconHeight: 100,
|
||||
title: 'Connect with Top\nProfessionals',
|
||||
title: 'Connect with Top Professionals',
|
||||
subtitle:
|
||||
'Professionals can list properties and find qualified\nbuyers faster than ever.',
|
||||
'Professionals can list properties and find qualified buyers faster than ever.',
|
||||
),
|
||||
_OnboardingData(
|
||||
bgAsset: 'assets/images/onboarding_2_bg.svg',
|
||||
iconAsset: 'assets/images/onboarding_2_icon.svg',
|
||||
iconWidth: 83,
|
||||
iconHeight: 75,
|
||||
title: 'Real help. Real professionals.\nInstantly.',
|
||||
title: 'Real help. Real professionals. Instantly.',
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
// Illustration section - takes upper portion
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 0, top: 8),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width - 24,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Background oval/pill shape with orange gradient
|
||||
SvgPicture.asset(
|
||||
// Scale the illustration to ~38% of screen height so it shrinks on small
|
||||
// devices, leaving room for the title + subtitle without overflow.
|
||||
final screenHeight = MediaQuery.of(context).size.height;
|
||||
final illustrationHeight = (screenHeight * 0.38).clamp(220.0, 346.0);
|
||||
|
||||
return SingleChildScrollView(
|
||||
physics: const ClampingScrollPhysics(),
|
||||
child: Column(
|
||||
children: [
|
||||
// Illustration section
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width - 24,
|
||||
height: illustrationHeight,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Background oval/pill shape with orange gradient
|
||||
Positioned.fill(
|
||||
child: SvgPicture.asset(
|
||||
data.bgAsset,
|
||||
width: 382,
|
||||
height: 346,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
// Blurred decoration - bottom right
|
||||
Positioned(
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
child: Container(
|
||||
width: 160,
|
||||
height: 160,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.accentOrange.withValues(alpha: 0.2),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.accentOrange
|
||||
.withValues(alpha: 0.15),
|
||||
blurRadius: 40,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Blurred decoration - bottom right
|
||||
Positioned(
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
child: Container(
|
||||
width: 120,
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.accentOrange.withValues(alpha: 0.2),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.accentOrange
|
||||
.withValues(alpha: 0.15),
|
||||
blurRadius: 40,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Blurred decoration - top left
|
||||
Positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
child: Container(
|
||||
width: 160,
|
||||
height: 160,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.accentOrange.withValues(alpha: 0.1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.accentOrange
|
||||
.withValues(alpha: 0.08),
|
||||
blurRadius: 40,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Blurred decoration - top left
|
||||
Positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
child: Container(
|
||||
width: 120,
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.accentOrange.withValues(alpha: 0.1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.accentOrange
|
||||
.withValues(alpha: 0.08),
|
||||
blurRadius: 40,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Main icon + small icons
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
data.iconAsset,
|
||||
width: data.iconWidth,
|
||||
height: data.iconHeight,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Small property icons row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/onboarding_small_icon_1.svg',
|
||||
width: 27,
|
||||
height: 27,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SvgPicture.asset(
|
||||
'assets/images/onboarding_small_icon_2.svg',
|
||||
width: 31.5,
|
||||
height: 31.5,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SvgPicture.asset(
|
||||
'assets/images/onboarding_small_icon_3.svg',
|
||||
width: 24,
|
||||
height: 27,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Main icon + small icons
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
data.iconAsset,
|
||||
width: data.iconWidth,
|
||||
height: data.iconHeight,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Small property icons row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/onboarding_small_icon_1.svg',
|
||||
width: 27,
|
||||
height: 27,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SvgPicture.asset(
|
||||
'assets/images/onboarding_small_icon_2.svg',
|
||||
width: 31.5,
|
||||
height: 31.5,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SvgPicture.asset(
|
||||
'assets/images/onboarding_small_icon_3.svg',
|
||||
width: 24,
|
||||
height: 27,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Text content section
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
// Text content section
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
// Title
|
||||
Text(
|
||||
data.title,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 32,
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Color(0xFF0F172A),
|
||||
height: 1.25,
|
||||
letterSpacing: -0.8,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 12),
|
||||
// Subtitle
|
||||
Text(
|
||||
data.subtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 16,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF475569),
|
||||
height: 1.625,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,47 +467,54 @@ class _ProfileSettingsTabState extends ConsumerState<ProfileSettingsTab> {
|
||||
}
|
||||
|
||||
return AlertDialog(
|
||||
scrollable: true,
|
||||
title: const Text('Change Email Address'),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text(
|
||||
'A verification link will be sent to your new email. Your email changes only after you click the link.',
|
||||
style: TextStyle(fontSize: 13),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text(
|
||||
'A verification link will be sent to your new email. Your email changes only after you click the link.',
|
||||
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(
|
||||
controller: newEmailCtl,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
autocorrect: false,
|
||||
textCapitalization: TextCapitalization.none,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'New Email',
|
||||
hintText: 'new@example.com',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TextField(
|
||||
controller: passwordCtl,
|
||||
obscureText: true,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
// Empty autofillHints prevents iOS from showing the
|
||||
// "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),
|
||||
TextField(
|
||||
controller: passwordCtl,
|
||||
obscureText: true,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Current Password',
|
||||
Text(
|
||||
localError!,
|
||||
style: const TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
if (localError != null) ...[
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
localError!,
|
||||
style: const TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:shared_preferences/shared_preferences.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});
|
||||
|
||||
@override
|
||||
State<SplashScreen> createState() => _SplashScreenState();
|
||||
ConsumerState<SplashScreen> createState() => _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends State<SplashScreen>
|
||||
class _SplashScreenState extends ConsumerState<SplashScreen>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final Animation<double> _fadeAnimation;
|
||||
@@ -43,11 +45,15 @@ class _SplashScreenState extends State<SplashScreen>
|
||||
final onboardingCompleted = prefs.getBool('onboarding_completed') ?? false;
|
||||
|
||||
if (!mounted) return;
|
||||
if (onboardingCompleted) {
|
||||
context.go('/home');
|
||||
} else {
|
||||
if (!onboardingCompleted) {
|
||||
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
|
||||
|
||||
@@ -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/splash/presentation/screens/splash_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/support_chat/presentation/screens/support_chat_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
|
||||
/// dashboard via the Edit Profile tab in the Profile section.
|
||||
/// Reactively switches between HomeScreen and AgentHomeScreen based on auth.
|
||||
/// Agents see their dashboard (profile view); users see the discovery home.
|
||||
class _HomeRouteWrapper extends ConsumerWidget {
|
||||
const _HomeRouteWrapper();
|
||||
|
||||
@@ -306,8 +307,10 @@ class _HomeRouteWrapper extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// Home icon always shows the User Home Page regardless of role.
|
||||
// Agents access their own dashboard via the Edit Profile tab in /profile.
|
||||
if (authState.status == AuthStatus.authenticated &&
|
||||
authState.user?.role == 'AGENT') {
|
||||
return const AgentHomeScreen();
|
||||
}
|
||||
return const HomeScreen();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user