From 0120508a4831aa42a6c98e497a984ebbc4038f6d Mon Sep 17 00:00:00 2001 From: mahmoodhamdi Date: Thu, 10 Oct 2024 23:57:11 +0300 Subject: [PATCH] Add Google authentication with Firebase integration and UI components --- analysis_options.yaml | 3 + android/app/build.gradle | 9 +- android/app/google-services.json | 37 ++++++- lib/app.dart | 3 +- lib/helpers/auth_with_google.dart | 145 +++++++++++++++++++++++++ lib/pages/login_page_.dart | 86 ++------------- lib/pages/register_page.dart | 84 ++------------ lib/utils/extensions/extensions.dart | 50 +++++++++ lib/widgets/full_width_button.dart | 42 +++++++ lib/widgets/google_sign_in_button.dart | 71 ++++++++++++ lib/widgets/login_button.dart | 42 +++++++ lib/widgets/sign_up_button.dart | 42 +++++++ pubspec.lock | 58 +++++++++- pubspec.yaml | 1 + 14 files changed, 516 insertions(+), 157 deletions(-) create mode 100644 lib/utils/extensions/extensions.dart create mode 100644 lib/widgets/full_width_button.dart create mode 100644 lib/widgets/google_sign_in_button.dart create mode 100644 lib/widgets/login_button.dart create mode 100644 lib/widgets/sign_up_button.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..fc8e8a3 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,6 +7,9 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + errors: + use_build_context_synchronously: ignore include: package:flutter_lints/flutter.yaml linter: diff --git a/android/app/build.gradle b/android/app/build.gradle index 49a075f..34f5f86 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -10,7 +10,7 @@ plugins { android { namespace = "com.example.firebase_multi_auth_guide" - compileSdk = flutter.compileSdkVersion + compileSdkVersion 34 ndkVersion = flutter.ndkVersion compileOptions { @@ -27,16 +27,15 @@ android { applicationId = "com.example.firebase_multi_auth_guide" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. - minSdk = flutter.minSdkVersion - targetSdk = flutter.targetSdkVersion + minSdkVersion 23 + targetSdkVersion 34 versionCode = flutter.versionCode versionName = flutter.versionName } buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.debug } } diff --git a/android/app/google-services.json b/android/app/google-services.json index 1106d0a..922597f 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -12,7 +12,28 @@ "package_name": "com.example.firebase_multi_auth_guide" } }, - "oauth_client": [], + "oauth_client": [ + { + "client_id": "1096731177237-btghj6gdb60cam4mc086ckhq81c7iaqe.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.example.firebase_multi_auth_guide", + "certificate_hash": "20121cceaa7c57b61696787d881a614b6b90fad7" + } + }, + { + "client_id": "1096731177237-pc42l34v7vavjcsqrvnbgq1amvdd85al.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.example.firebase_multi_auth_guide", + "certificate_hash": "e0382aa71e5b75a930791eac7ff65e3ae6011692" + } + }, + { + "client_id": "1096731177237-keco1o97ntmta8eaub2lvp380hghth7l.apps.googleusercontent.com", + "client_type": 3 + } + ], "api_key": [ { "current_key": "AIzaSyBEMJlekKjMYTzndbsUPwqZWq99NAnDEW0" @@ -20,7 +41,19 @@ ], "services": { "appinvite_service": { - "other_platform_oauth_client": [] + "other_platform_oauth_client": [ + { + "client_id": "1096731177237-keco1o97ntmta8eaub2lvp380hghth7l.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "1096731177237-i3gm5hk9gjvi5bhkma3jfnpmkii6o7fn.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.example.firebaseMultiAuthGuide" + } + } + ] } } } diff --git a/lib/app.dart b/lib/app.dart index 26c943c..5adf258 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -8,8 +8,7 @@ class FirebaseMultiAuthGuide extends StatelessWidget { Widget build(BuildContext context) { return const MaterialApp( title: 'Firebase Multi Auth Guide', - home:RegisterPage(), - + home: RegisterPage(), ); } } diff --git a/lib/helpers/auth_with_google.dart b/lib/helpers/auth_with_google.dart index e69de29..163367f 100644 --- a/lib/helpers/auth_with_google.dart +++ b/lib/helpers/auth_with_google.dart @@ -0,0 +1,145 @@ +import 'dart:developer'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:google_sign_in/google_sign_in.dart'; +import 'package:flutter/material.dart'; + +class AuthResult { + final bool success; + final UserCredential? userCredential; + final String message; + + AuthResult({ + required this.success, + this.userCredential, + required this.message, + }); +} + +class AuthWithGoogleHelper { + static Future signInWithGoogle() async { + try { + // Trigger the authentication flow + final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); + + // User canceled the sign-in flow + if (googleUser == null) { + return AuthResult( + success: false, + message: 'Sign in cancelled by user', + ); + } + + try { + // Obtain the auth details from the request + final GoogleSignInAuthentication googleAuth = + await googleUser.authentication; + + // Log user information for debugging + log('User Email: ${googleUser.email}'); + log('User Name: ${googleUser.displayName}'); + log('User Photo URL: ${googleUser.photoUrl ?? "No photo"}'); + + // Create a new credential + final credential = GoogleAuthProvider.credential( + accessToken: googleAuth.accessToken, + idToken: googleAuth.idToken, + ); + + // Sign in to Firebase with the Google credential + final userCredential = + await FirebaseAuth.instance.signInWithCredential(credential); + + return AuthResult( + success: true, + userCredential: userCredential, + message: 'Successfully signed in with Google', + ); + } catch (e) { + log('Error during Google authentication: $e'); + return AuthResult( + success: false, + message: 'Failed to authenticate with Google. Please try again.', + ); + } + } on FirebaseAuthException catch (e) { + log('FirebaseAuthException: $e'); + String message; + switch (e.code) { + case 'account-exists-with-different-credential': + message = + 'An account already exists with the same email but different sign-in credentials.'; + break; + case 'invalid-credential': + message = 'The authentication credential is invalid.'; + break; + case 'operation-not-allowed': + message = 'Google sign-in is not enabled for this project.'; + break; + case 'user-disabled': + message = 'This user account has been disabled.'; + break; + case 'user-not-found': + message = 'No user found for that email.'; + break; + default: + message = 'An unknown error occurred. Please try again.'; + } + return AuthResult(success: false, message: message); + } catch (e) { + log('Unexpected error during Google sign in: $e'); + return AuthResult( + success: false, + message: 'An unexpected error occurred. Please try again.', + ); + } + } +} + +// Extension to show SnackBars more easily +extension ShowSnackBar on BuildContext { + void showSuccessSnackBar(String message) { + ScaffoldMessenger.of(this).showSnackBar( + SnackBar( + content: Row( + children: [ + const Icon(Icons.check_circle, color: Colors.white), + const SizedBox(width: 8), + Expanded( + child: Text( + message, + style: const TextStyle(color: Colors.white), + ), + ), + ], + ), + backgroundColor: Colors.green.shade600, + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + duration: const Duration(seconds: 4), + ), + ); + } + + void showErrorSnackBar(String message) { + ScaffoldMessenger.of(this).showSnackBar( + SnackBar( + content: Row( + children: [ + const Icon(Icons.error, color: Colors.white), + const SizedBox(width: 8), + Expanded( + child: Text( + message, + style: const TextStyle(color: Colors.white), + ), + ), + ], + ), + backgroundColor: Colors.red.shade600, + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + duration: const Duration(seconds: 4), + ), + ); + } +} diff --git a/lib/pages/login_page_.dart b/lib/pages/login_page_.dart index 7a0d357..b4b340f 100644 --- a/lib/pages/login_page_.dart +++ b/lib/pages/login_page_.dart @@ -1,4 +1,7 @@ import 'package:firebase_multi_auth_guide/pages/register_page.dart'; +import 'package:firebase_multi_auth_guide/widgets/full_width_button.dart'; +import 'package:firebase_multi_auth_guide/widgets/google_sign_in_button.dart'; +import 'package:firebase_multi_auth_guide/widgets/login_button.dart'; import 'package:flutter/material.dart'; class LoginPage extends StatelessWidget { @@ -50,37 +53,32 @@ class LoginPage extends StatelessWidget { crossAxisSpacing: 16, childAspectRatio: 2.5, children: [ - _buildLoginButton( - text: 'Google', - color: Colors.red.shade400, - icon: Icons.g_mobiledata, - onPressed: () {}, - ), - _buildLoginButton( + const GoogleSignInButton(), + LoginButton( text: 'Facebook', color: Colors.blue.shade600, icon: Icons.facebook, onPressed: () {}, ), - _buildLoginButton( + LoginButton( text: 'Twitter', color: Colors.lightBlue.shade400, icon: Icons.alternate_email, onPressed: () {}, ), - _buildLoginButton( + LoginButton( text: 'GitHub', color: Colors.black87, icon: Icons.code, onPressed: () {}, ), - _buildLoginButton( + LoginButton( text: 'Microsoft', color: Colors.blue.shade800, icon: Icons.window, onPressed: () {}, ), - _buildLoginButton( + LoginButton( text: 'Apple', color: Colors.black, icon: Icons.apple, @@ -97,21 +95,21 @@ class LoginPage extends StatelessWidget { ), ), const SizedBox(height: 24), - _buildFullWidthButton( + FullWidthButton( text: 'Email and Password', color: Colors.deepPurple.shade400, icon: Icons.email, onPressed: () {}, ), const SizedBox(height: 16), - _buildFullWidthButton( + FullWidthButton( text: 'Phone Number', color: Colors.green.shade600, icon: Icons.phone, onPressed: () {}, ), const SizedBox(height: 16), - _buildFullWidthButton( + FullWidthButton( text: 'Continue as Guest', color: Colors.grey.shade700, icon: Icons.person_outline, @@ -151,64 +149,4 @@ class LoginPage extends StatelessWidget { ), ); } - - Widget _buildLoginButton({ - required String text, - required Color color, - required IconData icon, - required VoidCallback onPressed, - }) { - return ElevatedButton( - onPressed: onPressed, - style: ElevatedButton.styleFrom( - backgroundColor: color, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon, size: 20), - const SizedBox(width: 8), - Text( - text, - style: const TextStyle(fontSize: 14), - ), - ], - ), - ); - } - - Widget _buildFullWidthButton({ - required String text, - required Color color, - required IconData icon, - required VoidCallback onPressed, - }) { - return ElevatedButton( - onPressed: onPressed, - style: ElevatedButton.styleFrom( - backgroundColor: color, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon, size: 24), - const SizedBox(width: 12), - Text( - text, - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - ], - ), - ); - } } diff --git a/lib/pages/register_page.dart b/lib/pages/register_page.dart index 41ca257..9a64852 100644 --- a/lib/pages/register_page.dart +++ b/lib/pages/register_page.dart @@ -1,4 +1,7 @@ import 'package:firebase_multi_auth_guide/pages/login_page_.dart'; +import 'package:firebase_multi_auth_guide/widgets/google_sign_in_button.dart'; +import 'package:firebase_multi_auth_guide/widgets/full_width_button.dart'; +import 'package:firebase_multi_auth_guide/widgets/sign_up_button.dart'; import 'package:flutter/material.dart'; class RegisterPage extends StatelessWidget { @@ -50,37 +53,32 @@ class RegisterPage extends StatelessWidget { crossAxisSpacing: 16, childAspectRatio: 2.5, children: [ - _buildSignUpButton( - text: 'Google', - color: Colors.red.shade400, - icon: Icons.g_mobiledata, - onPressed: () {}, - ), - _buildSignUpButton( + const GoogleSignInButton(), + SignUpButton( text: 'Facebook', color: Colors.blue.shade600, icon: Icons.facebook, onPressed: () {}, ), - _buildSignUpButton( + SignUpButton( text: 'Twitter', color: Colors.lightBlue.shade400, icon: Icons.alternate_email, onPressed: () {}, ), - _buildSignUpButton( + SignUpButton( text: 'GitHub', color: Colors.black87, icon: Icons.code, onPressed: () {}, ), - _buildSignUpButton( + SignUpButton( text: 'Microsoft', color: Colors.blue.shade800, icon: Icons.window, onPressed: () {}, ), - _buildSignUpButton( + SignUpButton( text: 'Apple', color: Colors.black, icon: Icons.apple, @@ -97,14 +95,14 @@ class RegisterPage extends StatelessWidget { ), ), const SizedBox(height: 24), - _buildFullWidthButton( + FullWidthButton( text: 'Email and Password', color: Colors.deepPurple.shade400, icon: Icons.email, onPressed: () {}, ), const SizedBox(height: 16), - _buildFullWidthButton( + FullWidthButton( text: 'Phone Number', color: Colors.green.shade600, icon: Icons.phone, @@ -144,64 +142,4 @@ class RegisterPage extends StatelessWidget { ), ); } - - Widget _buildSignUpButton({ - required String text, - required Color color, - required IconData icon, - required VoidCallback onPressed, - }) { - return ElevatedButton( - onPressed: onPressed, - style: ElevatedButton.styleFrom( - backgroundColor: color, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon, size: 20), - const SizedBox(width: 8), - Text( - text, - style: const TextStyle(fontSize: 14), - ), - ], - ), - ); - } - - Widget _buildFullWidthButton({ - required String text, - required Color color, - required IconData icon, - required VoidCallback onPressed, - }) { - return ElevatedButton( - onPressed: onPressed, - style: ElevatedButton.styleFrom( - backgroundColor: color, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon, size: 24), - const SizedBox(width: 12), - Text( - text, - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - ], - ), - ); - } } diff --git a/lib/utils/extensions/extensions.dart b/lib/utils/extensions/extensions.dart new file mode 100644 index 0000000..f87fa3d --- /dev/null +++ b/lib/utils/extensions/extensions.dart @@ -0,0 +1,50 @@ +// Extension to show SnackBars more easily +import 'package:flutter/material.dart'; + +extension ShowSnackBar on BuildContext { + void showSuccessSnackBar(String message) { + ScaffoldMessenger.of(this).showSnackBar( + SnackBar( + content: Row( + children: [ + const Icon(Icons.check_circle, color: Colors.white), + const SizedBox(width: 8), + Expanded( + child: Text( + message, + style: const TextStyle(color: Colors.white), + ), + ), + ], + ), + backgroundColor: Colors.green.shade600, + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + duration: const Duration(seconds: 4), + ), + ); + } + + void showErrorSnackBar(String message) { + ScaffoldMessenger.of(this).showSnackBar( + SnackBar( + content: Row( + children: [ + const Icon(Icons.error, color: Colors.white), + const SizedBox(width: 8), + Expanded( + child: Text( + message, + style: const TextStyle(color: Colors.white), + ), + ), + ], + ), + backgroundColor: Colors.red.shade600, + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + duration: const Duration(seconds: 4), + ), + ); + } +} diff --git a/lib/widgets/full_width_button.dart b/lib/widgets/full_width_button.dart new file mode 100644 index 0000000..789f89b --- /dev/null +++ b/lib/widgets/full_width_button.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +class FullWidthButton extends StatelessWidget { + final String text; + final Color color; + final IconData icon; + final VoidCallback onPressed; + + const FullWidthButton({ + super.key, + required this.text, + required this.color, + required this.icon, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: color, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: 24), + const SizedBox(width: 12), + Text( + text, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/google_sign_in_button.dart b/lib/widgets/google_sign_in_button.dart new file mode 100644 index 0000000..0c240f9 --- /dev/null +++ b/lib/widgets/google_sign_in_button.dart @@ -0,0 +1,71 @@ +import 'dart:developer'; + +import 'package:firebase_multi_auth_guide/helpers/auth_with_google.dart'; +import 'package:firebase_multi_auth_guide/pages/home_page.dart'; +import 'package:firebase_multi_auth_guide/widgets/sign_up_button.dart'; +import 'package:flutter/material.dart'; + +class GoogleSignInButton extends StatelessWidget { + const GoogleSignInButton({super.key}); + + @override + Widget build(BuildContext context) { + return SignUpButton( + text: 'Google', + color: Colors.red.shade400, + icon: Icons.g_mobiledata, + onPressed: () async { + // Show loading indicator + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const Center( + child: CircularProgressIndicator(), + ), + ); + + try { + final result = await AuthWithGoogleHelper.signInWithGoogle(); + + // Always pop the loading dialog + Navigator.pop(context); + + if (result.success) { + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(result.message)), + ); + + // Navigate to home page + Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => HomePage( + title: + 'Welcome, ${result.userCredential?.user?.displayName ?? "User"}!', + ), + ), + ); + } else { + // Show error message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(result.message)), + ); + } + } catch (e) { + // Pop loading dialog in case of unexpected error + Navigator.pop(context); + + // Show error message + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('An unexpected error occurred. Please try again.'), + ), + ); + + // Log the error + log('Error during Google sign in: $e'); + } + }, + ); + } +} diff --git a/lib/widgets/login_button.dart b/lib/widgets/login_button.dart new file mode 100644 index 0000000..59234ee --- /dev/null +++ b/lib/widgets/login_button.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +class LoginButton extends StatelessWidget { + final String text; + final Color color; + final IconData icon; + final VoidCallback onPressed; + + const LoginButton({ + super.key, + required this.text, + required this.color, + required this.icon, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: color, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: 20), + const SizedBox(width: 8), + Text( + text, + style: const TextStyle(fontSize: 14), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/sign_up_button.dart b/lib/widgets/sign_up_button.dart new file mode 100644 index 0000000..1bc2701 --- /dev/null +++ b/lib/widgets/sign_up_button.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +class SignUpButton extends StatelessWidget { + final String text; + final Color color; + final IconData icon; + final VoidCallback onPressed; + + const SignUpButton({ + super.key, + required this.text, + required this.color, + required this.icon, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: color, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: 20), + const SizedBox(width: 8), + Text( + text, + style: const TextStyle(fontSize: 14), + ), + ], + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index de8e68b..3887972 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -136,6 +136,62 @@ packages: description: flutter source: sdk version: "0.0.0" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6" + url: "https://pub.dev" + source: hosted + version: "0.3.1+4" + google_sign_in: + dependency: "direct main" + description: + name: google_sign_in + sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f" + url: "https://pub.dev" + source: hosted + version: "6.2.1" + google_sign_in_android: + dependency: transitive + description: + name: google_sign_in_android + sha256: "0928059d2f0840f63c7b07a30cf73b593ae872cdd0dbd46d1b9ba878d2599c01" + url: "https://pub.dev" + source: hosted + version: "6.1.33" + google_sign_in_ios: + dependency: transitive + description: + name: google_sign_in_ios + sha256: "83f015169102df1ab2905cf8abd8934e28f87db9ace7a5fa676998842fed228a" + url: "https://pub.dev" + source: hosted + version: "5.7.8" + google_sign_in_platform_interface: + dependency: transitive + description: + name: google_sign_in_platform_interface + sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971" + url: "https://pub.dev" + source: hosted + version: "2.4.5" + google_sign_in_web: + dependency: transitive + description: + name: google_sign_in_web + sha256: "042805a21127a85b0dc46bba98a37926f17d2439720e8a459d27045d8ef68055" + url: "https://pub.dev" + source: hosted + version: "0.12.4+2" + http: + dependency: transitive + description: + name: http + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" + source: hosted + version: "1.2.2" http_parser: dependency: transitive description: @@ -303,4 +359,4 @@ packages: version: "1.1.0" sdks: dart: ">=3.5.3 <4.0.0" - flutter: ">=3.22.0" + flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 03dc3c4..20a5d4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: cupertino_icons: ^1.0.8 firebase_core: ^3.6.0 firebase_auth: ^5.3.1 + google_sign_in: ^6.2.1 dev_dependencies: flutter_test: