feat: implement social login functionality with Google, Facebook, and Twitter, including API integration and platform-specific configurations.

This commit is contained in:
pradeepkumar
2026-03-26 00:07:31 +05:30
parent ad2d50138a
commit c5f905b4d3
14 changed files with 479 additions and 14 deletions

View File

@@ -37,6 +37,27 @@
<meta-data <meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id" android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel" /> android:value="high_importance_channel" />
<!-- Facebook Login -->
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
<meta-data
android:name="com.facebook.sdk.ClientToken"
android:value="@string/facebook_client_token" />
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="@string/app_name" />
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/fb_login_protocol_scheme" />
</intent-filter>
</activity>
</application> </application>
<!-- Required to query activities that can process text, see: <!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and https://developer.android.com/training/package-visibility and

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="facebook_app_id">2124098594797306</string>
<string name="facebook_client_token">c7ed8c0d68a517cca1cd0e6668282410</string>
<string name="fb_login_protocol_scheme">fb2124098594797306</string>
</resources>

View File

@@ -1,6 +1,24 @@
PODS: PODS:
- AppAuth (1.7.6):
- AppAuth/Core (= 1.7.6)
- AppAuth/ExternalUserAgent (= 1.7.6)
- AppAuth/Core (1.7.6)
- AppAuth/ExternalUserAgent (1.7.6):
- AppAuth/Core
- AppCheckCore (11.2.0):
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0)
- PromisesObjC (~> 2.4)
- emoji_picker_flutter (0.0.1): - emoji_picker_flutter (0.0.1):
- Flutter - Flutter
- FBAEMKit (18.0.2):
- FBSDKCoreKit_Basics (= 18.0.2)
- FBSDKCoreKit (18.0.2):
- FBAEMKit (= 18.0.2)
- FBSDKCoreKit_Basics (= 18.0.2)
- FBSDKCoreKit_Basics (18.0.2)
- FBSDKLoginKit (18.0.2):
- FBSDKCoreKit (= 18.0.2)
- 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):
@@ -34,13 +52,27 @@ PODS:
- GoogleUtilities/UserDefaults (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_facebook_auth (7.1.5):
- FBSDKLoginKit (= 18.0.2)
- Flutter
- flutter_local_notifications (0.0.1): - flutter_local_notifications (0.0.1):
- Flutter - Flutter
- flutter_secure_storage (6.0.0): - flutter_secure_storage (6.0.0):
- Flutter - Flutter
- google_sign_in_ios (0.0.1):
- AppAuth (>= 1.7.4)
- Flutter
- FlutterMacOS
- GoogleSignIn (~> 8.0)
- GTMSessionFetcher (>= 3.4.0)
- GoogleDataTransport (10.1.0): - GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4) - PromisesObjC (~> 2.4)
- GoogleSignIn (8.0.0):
- AppAuth (< 2.0, >= 1.7.3)
- AppCheckCore (~> 11.0)
- GTMAppAuth (< 5.0, >= 4.1.1)
- GTMSessionFetcher/Core (~> 3.3)
- GoogleUtilities/AppDelegateSwizzler (8.1.0): - GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Logger - GoogleUtilities/Logger
@@ -65,6 +97,14 @@ PODS:
- GoogleUtilities/UserDefaults (8.1.0): - GoogleUtilities/UserDefaults (8.1.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Privacy - GoogleUtilities/Privacy
- GTMAppAuth (4.1.1):
- AppAuth/Core (~> 1.7)
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
- GTMSessionFetcher (3.5.0):
- GTMSessionFetcher/Full (= 3.5.0)
- GTMSessionFetcher/Core (3.5.0)
- GTMSessionFetcher/Full (3.5.0):
- GTMSessionFetcher/Core
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- nanopb (3.30910.0): - nanopb (3.30910.0):
@@ -84,6 +124,8 @@ PODS:
- sqflite_darwin (0.0.4): - sqflite_darwin (0.0.4):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- twitter_login (0.0.1):
- Flutter
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
- webview_flutter_wkwebview (0.0.1): - webview_flutter_wkwebview (0.0.1):
@@ -95,25 +137,37 @@ DEPENDENCIES:
- 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`)
- flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- 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`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/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`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- twitter_login (from `.symlinks/plugins/twitter_login/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- AppAuth
- AppCheckCore
- FBAEMKit
- FBSDKCoreKit
- FBSDKCoreKit_Basics
- FBSDKLoginKit
- Firebase - Firebase
- FirebaseCore - FirebaseCore
- FirebaseCoreInternal - FirebaseCoreInternal
- FirebaseInstallations - FirebaseInstallations
- FirebaseMessaging - FirebaseMessaging
- GoogleDataTransport - GoogleDataTransport
- GoogleSignIn
- GoogleUtilities - GoogleUtilities
- GTMAppAuth
- GTMSessionFetcher
- nanopb - nanopb
- PromisesObjC - PromisesObjC
@@ -126,10 +180,14 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_messaging/ios" :path: ".symlinks/plugins/firebase_messaging/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_facebook_auth:
:path: ".symlinks/plugins/flutter_facebook_auth/ios"
flutter_local_notifications: flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios" :path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_secure_storage: flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios" :path: ".symlinks/plugins/flutter_secure_storage/ios"
google_sign_in_ios:
: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"
package_info_plus: package_info_plus:
@@ -140,13 +198,21 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin" :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin: sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin" :path: ".symlinks/plugins/sqflite_darwin/darwin"
twitter_login:
:path: ".symlinks/plugins/twitter_login/ios"
url_launcher_ios: url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
webview_flutter_wkwebview: webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
SPEC CHECKSUMS: SPEC CHECKSUMS:
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
emoji_picker_flutter: 8e50ec5caac456a23a78637e02c6293ea0ac8771 emoji_picker_flutter: 8e50ec5caac456a23a78637e02c6293ea0ac8771
FBAEMKit: 56a4f5a9607957b547b6458b17a98c5bb2736367
FBSDKCoreKit: 54f1240567e16862ab71c53c33dc347db5c524c5
FBSDKCoreKit_Basics: eb530cd6a79030dbb11df460aafb2767bcad9028
FBSDKLoginKit: 6a70798793e499335a98fab1a758d7da34af196e
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c
firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7 firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7
@@ -155,10 +221,15 @@ SPEC CHECKSUMS:
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843 FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09 FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_facebook_auth: 08ab1a6be157d7fce8348b881787e7608f763267
flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
@@ -166,6 +237,7 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
twitter_login: 2794db69b7640681171b17b3c2c84ad9dfb4a57f
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa
webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4 webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4

View File

@@ -47,9 +47,26 @@
</array> </array>
</dict> </dict>
</dict> </dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb2124098594797306</string>
</array>
</dict>
</array>
<key>FacebookAppID</key>
<string>2124098594797306</string>
<key>FacebookClientToken</key>
<string>c7ed8c0d68a517cca1cd0e6668282410</string>
<key>FacebookDisplayName</key>
<string>RE-Quest</string>
<key>LSApplicationQueriesSchemes</key> <key>LSApplicationQueriesSchemes</key>
<array> <array>
<string>mailto</string> <string>mailto</string>
<string>fbapi</string>
<string>fb-messenger-share-api</string>
</array> </array>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to set your profile photo.</string> <string>We need access to your photo library to set your profile photo.</string>

View File

@@ -39,4 +39,7 @@ class ApiConstants {
// 2FA // 2FA
static const String twoFactorVerify = '/auth/2fa/verify'; static const String twoFactorVerify = '/auth/2fa/verify';
static const String twoFactorVerifyBackup = '/auth/2fa/verify-backup'; static const String twoFactorVerifyBackup = '/auth/2fa/verify-backup';
// Social Auth
static const String authSocial = '/auth/social';
} }

View File

@@ -308,6 +308,13 @@ class _AgentDetailScreenState extends ConsumerState<AgentDetailScreen> {
if (!mounted) return; if (!mounted) return;
if (conversationId != null) { if (conversationId != null) {
context.push('/messages/chat/$conversationId'); context.push('/messages/chat/$conversationId');
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('You need to be connected to send messages.'),
behavior: SnackBarBehavior.floating,
),
);
} }
} finally { } finally {
if (mounted) setState(() => _isStartingConversation = false); if (mounted) setState(() => _isStartingConversation = false);

View File

@@ -130,6 +130,44 @@ class AuthRepository {
} }
} }
/// Social auth (Google, Facebook, etc.) — calls POST /auth/social
Future<UserModel> socialAuth({
required String provider,
required String providerId,
required String email,
String? name,
String? avatar,
}) async {
try {
final response = await _dio.post(
ApiConstants.authSocial,
data: {
'provider': provider,
'providerId': providerId,
'email': email,
if (name != null) 'name': name,
if (avatar != null) 'avatar': avatar,
},
);
final data = response.data['data'] as Map<String, dynamic>;
final authResponse = AuthResponse.fromJson(data);
await SecureStorage.saveTokens(
accessToken: authResponse.accessToken,
refreshToken: authResponse.refreshToken,
);
return authResponse.user;
} on DioException catch (e) {
if (e.error is ApiException) throw e.error as ApiException;
throw ApiException(
message: e.message ?? 'Social login failed',
statusCode: e.response?.statusCode,
);
}
}
/// Register returns { message, user } — no tokens. /// Register returns { message, user } — no tokens.
/// The user must verify their email before logging in. /// The user must verify their email before logging in.
Future<Map<String, dynamic>> register(RegisterRequest request) async { Future<Map<String, dynamic>> register(RegisterRequest request) async {

View File

@@ -1,4 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:real_estate_mobile/core/network/api_client.dart'; import 'package:real_estate_mobile/core/network/api_client.dart';
import 'package:real_estate_mobile/core/network/api_exceptions.dart'; import 'package:real_estate_mobile/core/network/api_exceptions.dart';
import 'package:real_estate_mobile/core/services/push_notification_service.dart'; import 'package:real_estate_mobile/core/services/push_notification_service.dart';
@@ -7,6 +10,7 @@ import 'package:real_estate_mobile/features/auth/data/auth_repository.dart';
import 'package:real_estate_mobile/features/auth/data/models/register_request.dart'; import 'package:real_estate_mobile/features/auth/data/models/register_request.dart';
import 'package:real_estate_mobile/features/auth/data/models/user_model.dart'; import 'package:real_estate_mobile/features/auth/data/models/user_model.dart';
import 'package:real_estate_mobile/features/messaging/data/socket_service.dart'; import 'package:real_estate_mobile/features/messaging/data/socket_service.dart';
import 'package:twitter_login/twitter_login.dart';
// Auth state // Auth state
enum AuthStatus { initial, loading, authenticated, error } enum AuthStatus { initial, loading, authenticated, error }
@@ -170,6 +174,182 @@ class AuthNotifier extends StateNotifier<AuthState> {
} }
} }
Future<void> signInWithGoogle() async {
state = state.copyWith(
status: AuthStatus.loading,
errorMessage: null,
fieldErrors: [],
);
try {
final googleSignIn = GoogleSignIn(
scopes: ['email', 'profile'],
serverClientId: '721413957511-goij5fnpal64o86vcppmkh743jhiv4nj.apps.googleusercontent.com',
);
final account = await googleSignIn.signIn();
if (account == null) {
// User cancelled
state = state.copyWith(status: AuthStatus.initial);
return;
}
final user = await _repository.socialAuth(
provider: 'google',
providerId: account.id,
email: account.email,
name: account.displayName,
avatar: account.photoUrl,
);
state = state.copyWith(
status: AuthStatus.authenticated,
user: user,
);
_connectSocket();
_initPushNotifications();
} on ApiException catch (e) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: e.message,
);
} catch (e) {
debugPrint('Google sign-in error: $e');
state = state.copyWith(
status: AuthStatus.error,
errorMessage: 'Google sign-in failed. Please try again.',
);
}
}
Future<void> signInWithFacebook() async {
state = state.copyWith(
status: AuthStatus.loading,
errorMessage: null,
fieldErrors: [],
);
try {
final result = await FacebookAuth.instance.login(
permissions: ['email', 'public_profile'],
);
if (result.status == LoginStatus.cancelled) {
state = state.copyWith(status: AuthStatus.initial);
return;
}
if (result.status != LoginStatus.success) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: result.message ?? 'Facebook login failed',
);
return;
}
final userData = await FacebookAuth.instance.getUserData(
fields: 'id,name,email,picture.width(200)',
);
final email = userData['email'] as String?;
if (email == null || email.isEmpty) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: 'Email not available from Facebook. Please use email login.',
);
return;
}
final pictureData = userData['picture']?['data'];
final avatarUrl = pictureData?['url'] as String?;
final user = await _repository.socialAuth(
provider: 'facebook',
providerId: userData['id'] as String,
email: email,
name: userData['name'] as String?,
avatar: avatarUrl,
);
state = state.copyWith(
status: AuthStatus.authenticated,
user: user,
);
_connectSocket();
_initPushNotifications();
} on ApiException catch (e) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: e.message,
);
} catch (e) {
debugPrint('Facebook sign-in error: $e');
state = state.copyWith(
status: AuthStatus.error,
errorMessage: 'Facebook sign-in failed. Please try again.',
);
}
}
Future<void> signInWithTwitter() async {
state = state.copyWith(
status: AuthStatus.loading,
errorMessage: null,
fieldErrors: [],
);
try {
final twitterLogin = TwitterLogin(
apiKey: 'eWEzaDJ0al9lX1diOGY0ejRYeXM6MTpjaQ',
apiSecretKey: 'HadGMSRKKswfXOajbmggrnmLAn9cxJsl8GtFxGHqjX-CJ0A2Tu',
redirectURI: 'request://',
);
final authResult = await twitterLogin.login();
if (authResult.status == TwitterLoginStatus.cancelledByUser) {
state = state.copyWith(status: AuthStatus.initial);
return;
}
if (authResult.status != TwitterLoginStatus.loggedIn) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: authResult.errorMessage ?? 'Twitter login failed',
);
return;
}
final twitterUser = authResult.user!;
final user = await _repository.socialAuth(
provider: 'twitter',
providerId: twitterUser.id.toString(),
email: '${twitterUser.screenName}@twitter.placeholder',
name: twitterUser.name,
avatar: twitterUser.thumbnailImage,
);
state = state.copyWith(
status: AuthStatus.authenticated,
user: user,
);
_connectSocket();
_initPushNotifications();
} on ApiException catch (e) {
state = state.copyWith(
status: AuthStatus.error,
errorMessage: e.message,
);
} catch (e) {
debugPrint('Twitter sign-in error: $e');
state = state.copyWith(
status: AuthStatus.error,
errorMessage: 'Twitter sign-in failed. Please try again.',
);
}
}
Future<void> register({ Future<void> register({
required String name, required String name,
required String email, required String email,

View File

@@ -1,8 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.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 SocialLoginButtons extends StatelessWidget { class SocialLoginButtons extends ConsumerWidget {
const SocialLoginButtons({super.key}); const SocialLoginButtons({super.key});
void _showComingSoon(BuildContext context) { void _showComingSoon(BuildContext context) {
@@ -16,13 +18,16 @@ class SocialLoginButtons extends StatelessWidget {
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context, WidgetRef ref) {
final authState = ref.watch(authProvider);
final isLoading = authState.status == AuthStatus.loading;
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// Apple // Apple
_SocialButton( _SocialButton(
onTap: () => _showComingSoon(context), onTap: isLoading ? null : () => _showComingSoon(context),
child: const Icon( child: const Icon(
Icons.apple, Icons.apple,
size: 28, size: 28,
@@ -32,7 +37,9 @@ class SocialLoginButtons extends StatelessWidget {
const SizedBox(width: 32), const SizedBox(width: 32),
// Google // Google
_SocialButton( _SocialButton(
onTap: () => _showComingSoon(context), onTap: isLoading
? null
: () => ref.read(authProvider.notifier).signInWithGoogle(),
child: SvgPicture.asset( child: SvgPicture.asset(
'assets/icons/google.svg', 'assets/icons/google.svg',
width: 24, width: 24,
@@ -42,7 +49,9 @@ class SocialLoginButtons extends StatelessWidget {
const SizedBox(width: 32), const SizedBox(width: 32),
// X (Twitter) // X (Twitter)
_SocialButton( _SocialButton(
onTap: () => _showComingSoon(context), onTap: isLoading
? null
: () => ref.read(authProvider.notifier).signInWithTwitter(),
child: SvgPicture.asset( child: SvgPicture.asset(
'assets/icons/x.svg', 'assets/icons/x.svg',
width: 24, width: 24,
@@ -52,7 +61,9 @@ class SocialLoginButtons extends StatelessWidget {
const SizedBox(width: 32), const SizedBox(width: 32),
// Facebook // Facebook
_SocialButton( _SocialButton(
onTap: () => _showComingSoon(context), onTap: isLoading
? null
: () => ref.read(authProvider.notifier).signInWithFacebook(),
child: SvgPicture.asset( child: SvgPicture.asset(
'assets/icons/facebook.svg', 'assets/icons/facebook.svg',
width: 24, width: 24,
@@ -65,7 +76,7 @@ class SocialLoginButtons extends StatelessWidget {
} }
class _SocialButton extends StatelessWidget { class _SocialButton extends StatelessWidget {
final VoidCallback onTap; final VoidCallback? onTap;
final Widget child; final Widget child;
const _SocialButton({ const _SocialButton({
@@ -77,10 +88,13 @@ class _SocialButton extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,
child: SizedBox( child: Opacity(
width: 40, opacity: onTap != null ? 1.0 : 0.5,
height: 40, child: SizedBox(
child: Center(child: child), width: 40,
height: 40,
child: Center(child: child),
),
), ),
); );
} }

View File

@@ -327,7 +327,9 @@ class _TopProfessionalsSectionState
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
child: Column( child: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Name // Name
@@ -515,6 +517,7 @@ class _TopProfessionalsSectionState
), ),
], ],
), ),
),
), ),
), ),
], ],

View File

@@ -423,7 +423,7 @@ class MessagingNotifier extends StateNotifier<MessagingState> {
conversations: updatedConversations, conversations: updatedConversations,
isSending: false, isSending: false,
); );
} catch (_) { } catch (e) {
final messagesAfterError = final messagesAfterError =
List<ChatMessage>.from(state.messages[conversationId] ?? []); List<ChatMessage>.from(state.messages[conversationId] ?? []);
messagesAfterError.removeWhere((m) => m.id == tempId); messagesAfterError.removeWhere((m) => m.id == tempId);
@@ -432,10 +432,15 @@ class MessagingNotifier extends StateNotifier<MessagingState> {
Map<String, List<ChatMessage>>.from(state.messages) Map<String, List<ChatMessage>>.from(state.messages)
..[conversationId] = messagesAfterError; ..[conversationId] = messagesAfterError;
// Show specific error for connection-required failures
final errorMsg = e.toString().contains('connection')
? 'You must be connected to send messages'
: 'Failed to send message';
state = state.copyWith( state = state.copyWith(
messages: errorMessages, messages: errorMessages,
isSending: false, isSending: false,
error: 'Failed to send message', error: errorMsg,
); );
} }
} }

View File

@@ -6,29 +6,35 @@ import FlutterMacOS
import Foundation import Foundation
import emoji_picker_flutter import emoji_picker_flutter
import facebook_auth_desktop
import file_selector_macos import file_selector_macos
import firebase_core import firebase_core
import firebase_messaging import firebase_messaging
import flutter_local_notifications import flutter_local_notifications
import flutter_secure_storage_macos import flutter_secure_storage_macos
import google_sign_in_ios
import package_info_plus import package_info_plus
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
import sqflite_darwin import sqflite_darwin
import twitter_login
import url_launcher_macos import url_launcher_macos
import webview_flutter_wkwebview import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin")) EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin"))
FacebookAuthDesktopPlugin.register(with: registry.registrar(forPlugin: "FacebookAuthDesktopPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
TwitterLoginPlugin.register(with: registry.registrar(forPlugin: "TwitterLoginPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin")) WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
} }

View File

@@ -289,6 +289,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.4.0" version: "4.4.0"
facebook_auth_desktop:
dependency: transitive
description:
name: facebook_auth_desktop
sha256: "219d559a33891e937c1913430505eae01fb946cb35729167bbdc747e3ebbd9ff"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@@ -422,6 +430,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.2.1" version: "5.2.1"
flutter_facebook_auth:
dependency: "direct main"
description:
name: flutter_facebook_auth
sha256: f4652480123fa22d02c8fd3cb890246bf3358d1e9928fc5f576c3d1b0faf2acc
url: "https://pub.dev"
source: hosted
version: "7.1.5"
flutter_facebook_auth_platform_interface:
dependency: transitive
description:
name: flutter_facebook_auth_platform_interface
sha256: e04b8dbfa77702bea45a79993163ad5d20b2c0084109bec591fdc2b9ee505779
url: "https://pub.dev"
source: hosted
version: "6.1.2"
flutter_facebook_auth_web:
dependency: transitive
description:
name: flutter_facebook_auth_web
sha256: f682400d61cf8d52dd8b6458b5ee106ed57e95309a117dc32875d3da129ce47c
url: "https://pub.dev"
source: hosted
version: "6.1.2"
flutter_flavorizr: flutter_flavorizr:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -592,6 +624,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.8.1" version: "14.8.1"
google_identity_services_web:
dependency: transitive
description:
name: google_identity_services_web
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
url: "https://pub.dev"
source: hosted
version: "0.3.3+1"
google_sign_in:
dependency: "direct main"
description:
name: google_sign_in
sha256: d0a2c3bcb06e607bb11e4daca48bd4b6120f0bbc4015ccebbe757d24ea60ed2a
url: "https://pub.dev"
source: hosted
version: "6.3.0"
google_sign_in_android:
dependency: transitive
description:
name: google_sign_in_android
sha256: d5e23c56a4b84b6427552f1cf3f98f716db3b1d1a647f16b96dbb5b93afa2805
url: "https://pub.dev"
source: hosted
version: "6.2.1"
google_sign_in_ios:
dependency: transitive
description:
name: google_sign_in_ios
sha256: "102005f498ce18442e7158f6791033bbc15ad2dcc0afa4cf4752e2722a516c96"
url: "https://pub.dev"
source: hosted
version: "5.9.0"
google_sign_in_platform_interface:
dependency: transitive
description:
name: google_sign_in_platform_interface
sha256: "5f6f79cf139c197261adb6ac024577518ae48fdff8e53205c5373b5f6430a8aa"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
google_sign_in_web:
dependency: transitive
description:
name: google_sign_in_web
sha256: "460547beb4962b7623ac0fb8122d6b8268c951cf0b646dd150d60498430e4ded"
url: "https://pub.dev"
source: hosted
version: "0.12.4+4"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@@ -1269,6 +1349,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
twitter_login:
dependency: "direct main"
description:
name: twitter_login
sha256: "31ff9db2e37eda878b876a4ce6d1525f51d34b6cd9de9aa185b07027a23ab95b"
url: "https://pub.dev"
source: hosted
version: "4.4.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:

View File

@@ -45,6 +45,11 @@ dependencies:
firebase_messaging: ^15.2.4 firebase_messaging: ^15.2.4
flutter_local_notifications: ^18.0.1 flutter_local_notifications: ^18.0.1
# Social Login
google_sign_in: ^6.2.2
flutter_facebook_auth: ^7.1.1
twitter_login: ^4.4.2
# Pin to avoid objective_c crash on iOS 26 simulator # Pin to avoid objective_c crash on iOS 26 simulator
path_provider_foundation: 2.4.0 path_provider_foundation: 2.4.0