feat: implement social login functionality with Google, Facebook, and Twitter, including API integration and platform-specific configurations.
This commit is contained in:
@@ -37,6 +37,27 @@
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
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>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
|
||||
6
android/app/src/main/res/values/strings.xml
Normal file
6
android/app/src/main/res/values/strings.xml
Normal 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>
|
||||
@@ -1,6 +1,24 @@
|
||||
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):
|
||||
- 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):
|
||||
- FirebaseCore (~> 11.15.0)
|
||||
- Firebase/Messaging (11.15.0):
|
||||
@@ -34,13 +52,27 @@ PODS:
|
||||
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||
- nanopb (~> 3.30910.0)
|
||||
- Flutter (1.0.0)
|
||||
- flutter_facebook_auth (7.1.5):
|
||||
- FBSDKLoginKit (= 18.0.2)
|
||||
- Flutter
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- Flutter
|
||||
- flutter_secure_storage (6.0.0):
|
||||
- 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):
|
||||
- nanopb (~> 3.30910.0)
|
||||
- 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/Environment
|
||||
- GoogleUtilities/Logger
|
||||
@@ -65,6 +97,14 @@ PODS:
|
||||
- GoogleUtilities/UserDefaults (8.1.0):
|
||||
- GoogleUtilities/Logger
|
||||
- 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):
|
||||
- Flutter
|
||||
- nanopb (3.30910.0):
|
||||
@@ -84,6 +124,8 @@ PODS:
|
||||
- sqflite_darwin (0.0.4):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- twitter_login (0.0.1):
|
||||
- Flutter
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
@@ -95,25 +137,37 @@ DEPENDENCIES:
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/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`)
|
||||
- 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`)
|
||||
- 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`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- AppAuth
|
||||
- AppCheckCore
|
||||
- FBAEMKit
|
||||
- FBSDKCoreKit
|
||||
- FBSDKCoreKit_Basics
|
||||
- FBSDKLoginKit
|
||||
- Firebase
|
||||
- FirebaseCore
|
||||
- FirebaseCoreInternal
|
||||
- FirebaseInstallations
|
||||
- FirebaseMessaging
|
||||
- GoogleDataTransport
|
||||
- GoogleSignIn
|
||||
- GoogleUtilities
|
||||
- GTMAppAuth
|
||||
- GTMSessionFetcher
|
||||
- nanopb
|
||||
- PromisesObjC
|
||||
|
||||
@@ -126,10 +180,14 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/firebase_messaging/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_facebook_auth:
|
||||
:path: ".symlinks/plugins/flutter_facebook_auth/ios"
|
||||
flutter_local_notifications:
|
||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||
flutter_secure_storage:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
google_sign_in_ios:
|
||||
:path: ".symlinks/plugins/google_sign_in_ios/darwin"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
package_info_plus:
|
||||
@@ -140,13 +198,21 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
sqflite_darwin:
|
||||
:path: ".symlinks/plugins/sqflite_darwin/darwin"
|
||||
twitter_login:
|
||||
:path: ".symlinks/plugins/twitter_login/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
webview_flutter_wkwebview:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
|
||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
||||
emoji_picker_flutter: 8e50ec5caac456a23a78637e02c6293ea0ac8771
|
||||
FBAEMKit: 56a4f5a9607957b547b6458b17a98c5bb2736367
|
||||
FBSDKCoreKit: 54f1240567e16862ab71c53c33dc347db5c524c5
|
||||
FBSDKCoreKit_Basics: eb530cd6a79030dbb11df460aafb2767bcad9028
|
||||
FBSDKLoginKit: 6a70798793e499335a98fab1a758d7da34af196e
|
||||
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
|
||||
firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c
|
||||
firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7
|
||||
@@ -155,10 +221,15 @@ SPEC CHECKSUMS:
|
||||
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
|
||||
FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_facebook_auth: 08ab1a6be157d7fce8348b881787e7608f763267
|
||||
flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9
|
||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||
google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4
|
||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
|
||||
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
||||
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
|
||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
@@ -166,6 +237,7 @@ SPEC CHECKSUMS:
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
|
||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||
twitter_login: 2794db69b7640681171b17b3c2c84ad9dfb4a57f
|
||||
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa
|
||||
webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4
|
||||
|
||||
|
||||
@@ -47,9 +47,26 @@
|
||||
</array>
|
||||
</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>
|
||||
<array>
|
||||
<string>mailto</string>
|
||||
<string>fbapi</string>
|
||||
<string>fb-messenger-share-api</string>
|
||||
</array>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>We need access to your photo library to set your profile photo.</string>
|
||||
|
||||
@@ -39,4 +39,7 @@ class ApiConstants {
|
||||
// 2FA
|
||||
static const String twoFactorVerify = '/auth/2fa/verify';
|
||||
static const String twoFactorVerifyBackup = '/auth/2fa/verify-backup';
|
||||
|
||||
// Social Auth
|
||||
static const String authSocial = '/auth/social';
|
||||
}
|
||||
|
||||
@@ -308,6 +308,13 @@ class _AgentDetailScreenState extends ConsumerState<AgentDetailScreen> {
|
||||
if (!mounted) return;
|
||||
if (conversationId != null) {
|
||||
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 {
|
||||
if (mounted) setState(() => _isStartingConversation = false);
|
||||
|
||||
@@ -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.
|
||||
/// The user must verify their email before logging in.
|
||||
Future<Map<String, dynamic>> register(RegisterRequest request) async {
|
||||
|
||||
@@ -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: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_exceptions.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/user_model.dart';
|
||||
import 'package:real_estate_mobile/features/messaging/data/socket_service.dart';
|
||||
import 'package:twitter_login/twitter_login.dart';
|
||||
|
||||
// Auth state
|
||||
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({
|
||||
required String name,
|
||||
required String email,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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});
|
||||
|
||||
void _showComingSoon(BuildContext context) {
|
||||
@@ -16,13 +18,16 @@ class SocialLoginButtons extends StatelessWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final authState = ref.watch(authProvider);
|
||||
final isLoading = authState.status == AuthStatus.loading;
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Apple
|
||||
_SocialButton(
|
||||
onTap: () => _showComingSoon(context),
|
||||
onTap: isLoading ? null : () => _showComingSoon(context),
|
||||
child: const Icon(
|
||||
Icons.apple,
|
||||
size: 28,
|
||||
@@ -32,7 +37,9 @@ class SocialLoginButtons extends StatelessWidget {
|
||||
const SizedBox(width: 32),
|
||||
// Google
|
||||
_SocialButton(
|
||||
onTap: () => _showComingSoon(context),
|
||||
onTap: isLoading
|
||||
? null
|
||||
: () => ref.read(authProvider.notifier).signInWithGoogle(),
|
||||
child: SvgPicture.asset(
|
||||
'assets/icons/google.svg',
|
||||
width: 24,
|
||||
@@ -42,7 +49,9 @@ class SocialLoginButtons extends StatelessWidget {
|
||||
const SizedBox(width: 32),
|
||||
// X (Twitter)
|
||||
_SocialButton(
|
||||
onTap: () => _showComingSoon(context),
|
||||
onTap: isLoading
|
||||
? null
|
||||
: () => ref.read(authProvider.notifier).signInWithTwitter(),
|
||||
child: SvgPicture.asset(
|
||||
'assets/icons/x.svg',
|
||||
width: 24,
|
||||
@@ -52,7 +61,9 @@ class SocialLoginButtons extends StatelessWidget {
|
||||
const SizedBox(width: 32),
|
||||
// Facebook
|
||||
_SocialButton(
|
||||
onTap: () => _showComingSoon(context),
|
||||
onTap: isLoading
|
||||
? null
|
||||
: () => ref.read(authProvider.notifier).signInWithFacebook(),
|
||||
child: SvgPicture.asset(
|
||||
'assets/icons/facebook.svg',
|
||||
width: 24,
|
||||
@@ -65,7 +76,7 @@ class SocialLoginButtons extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _SocialButton extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback? onTap;
|
||||
final Widget child;
|
||||
|
||||
const _SocialButton({
|
||||
@@ -77,10 +88,13 @@ class _SocialButton extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Center(child: child),
|
||||
child: Opacity(
|
||||
opacity: onTap != null ? 1.0 : 0.5,
|
||||
child: SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Center(child: child),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -327,7 +327,9 @@ class _TopProfessionalsSectionState
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
|
||||
child: Column(
|
||||
child: SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Name
|
||||
@@ -515,6 +517,7 @@ class _TopProfessionalsSectionState
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -423,7 +423,7 @@ class MessagingNotifier extends StateNotifier<MessagingState> {
|
||||
conversations: updatedConversations,
|
||||
isSending: false,
|
||||
);
|
||||
} catch (_) {
|
||||
} catch (e) {
|
||||
final messagesAfterError =
|
||||
List<ChatMessage>.from(state.messages[conversationId] ?? []);
|
||||
messagesAfterError.removeWhere((m) => m.id == tempId);
|
||||
@@ -432,10 +432,15 @@ class MessagingNotifier extends StateNotifier<MessagingState> {
|
||||
Map<String, List<ChatMessage>>.from(state.messages)
|
||||
..[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(
|
||||
messages: errorMessages,
|
||||
isSending: false,
|
||||
error: 'Failed to send message',
|
||||
error: errorMsg,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,29 +6,35 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import emoji_picker_flutter
|
||||
import facebook_auth_desktop
|
||||
import file_selector_macos
|
||||
import firebase_core
|
||||
import firebase_messaging
|
||||
import flutter_local_notifications
|
||||
import flutter_secure_storage_macos
|
||||
import google_sign_in_ios
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
import sqflite_darwin
|
||||
import twitter_login
|
||||
import url_launcher_macos
|
||||
import webview_flutter_wkwebview
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin"))
|
||||
FacebookAuthDesktopPlugin.register(with: registry.registrar(forPlugin: "FacebookAuthDesktopPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
TwitterLoginPlugin.register(with: registry.registrar(forPlugin: "TwitterLoginPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
|
||||
}
|
||||
|
||||
88
pubspec.lock
88
pubspec.lock
@@ -289,6 +289,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -422,6 +430,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -592,6 +624,54 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1269,6 +1349,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -45,6 +45,11 @@ dependencies:
|
||||
firebase_messaging: ^15.2.4
|
||||
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
|
||||
path_provider_foundation: 2.4.0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user