Using Google Sign-in With Firebase In Flutter
In this article, we will add Firebase Authentication and Google Sign-in to a Flutter application, which will authenticate a user by logging in using the gmail account and then we will retrieve user information.
- Get Started With Google Sign-in
- Adding Firebase Auth And Google Sign-in To Flutter
- Enable Google Sign-in
- Generate SHA-1 Fingerprint
- Creating The Welcome Screen
- Creating The Google Sign-in Page
- Performing Google Sign-in
- Retrieve User Information
Get Started With Google Sign-in
This is the sixth article related to Firebase in Flutter, you can check the previous articles in the below links:
- Get Started With Firebase in Flutter
- Using Firebase Queries In Flutter
- Using Firebase Auth In Flutter
- Using Firebase Storage In Flutter
- Using Cloud Firestore In Flutter
- Using Firebase Cloud Messaging In Flutter
To know how to integrate Firebase with Flutter, you can check the first article in the above list. In the other two articles, I created a form using Flutter performed queries for the realtime database and authenticated users with Firebase, in the cloud firestore article, it was different code snippet related to Firestore and explaining each one. This article I will demonstrate how to use google_sign_in
and firebase_auth
.This article is aimed for the android phones.
Adding Firebase Auth And Google Sign-in To Flutter
As I said before, to check how to create a flutter project and add Firebase, then please check this article Get Started With Firebase in Flutter. Next, you need to add the following dependency to the pubspec.yaml
file:
1
2
3
4
5
6
7
8
9
10
11
12
dependencies:
cupertino_icons: ^1.0.8
firebase_auth: ^5.1.4
firebase_core: ^3.3.0
flutter:
sdk: flutter
font_awesome_flutter: ^10.7.0
google_sign_in: ^6.2.1
dev_dependencies:
flutter_test:
sdk: flutter
Click CTRL + S to save, and you have successfully added the above dependencies to your Flutter application!
Enable Google Sign-in
Now before working on the application, you need to navigate to the Firebase console and enable google sign-in. Therefore navigate to the Authentication tab and then click Sign-in method. You will find multiple providers, but for this tutorial you only need to enable the Google provider:
When enabling, you will also be asked to provide project support email which will be the presented to users when they are authenticating with Google. If you want to authenticate using email/password then check this tutorial: Using Firebase Auth In Flutter.
Generate SHA-1 Fingerprint
This is an important step. To be able to use Google sign-in, then you need to generate the SHA-1 key. This key will be used by Firebase to create an OAuth2 client and API key for your app. Therefore in the terminal, execute:
1
2
cd android
./gradlew signingReport
which will give you the following output:
1
2
3
4
5
6
7
8
9
> Task :app:signingReport
Variant: debug
Config: debug
Store: /Users/peter/.android/debug.keystore
Alias: AndroidDebugKey
MD5: <md5-key>
SHA1: <sha1-key>
SHA-256: <sha256-key>
Valid until: Tuesday, December 1, 2048
Copy the SHA-1 key and navigate to the Firebase console, then go to the Project settings, and add the key as shown in the image below:
Then download the google-services.json file again, and add it the project under android/app
.
After that execute flutter clean
. The above steps are really important, if not done correctly you will get the following error later on:
1
PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null)
Creating The Welcome Screen
The design of this application is based on this dribbble design by Ashlee Mckay. Even though, the design contains email/password field, for this tutorial we only care about the sign in with google.
First inside the main()
method add the following:
1
2
3
4
5
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
runApp(MyApp());
}
As usual, since we are using Firebase packages, then we need to call Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform)
to configure it. Then inside the MyApp
class, you need to do the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: Constants.title,
initialRoute: '/',
routes: Navigate.routes,
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
}
}
So here I created a util class called Constants
which will contain all string values used. For the routes
, I also created a Navigate
class which will contain all the routes
. The Navigate
class:
1
2
3
4
5
6
7
class Navigate {
static Map<String, Widget Function(BuildContext)> routes = {
'/' : (context) => WelcomePage(),
'/sign-in' : (context) => SignInPage(),
'/home' : (context) => HomePage()
};
}
Now, create a new file called welcome_page.dart
. Inside that file you would have WelcomePage
class which extends StatelessWidget
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class WelcomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
User? result = FirebaseAuth.instance.currentUser;
return Scaffold(
backgroundColor: Constants.kPrimaryColor,
body: AnnotatedRegion<SystemUiOverlayStyle>(
value: Constants.statusBarColor,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset("assets/images/main-img.png"),
So here we use the MediaQuery
to get the size of the screen, and we also use currentUser
which will return the currently logged in user. Then create an instance of AnnotatedRegion
, you need to use this class to be able to change the status bar and icon color. The statusBarColor
is a constant value in the class Constants
:
1
2
3
static const statusBarColor = SystemUiOverlayStyle(
statusBarColor: Constants.kPrimaryColor,
statusBarIconBrightness: Brightness.dark);
Inside the Column
widget, we would add all the children widget:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset("assets/images/main-img.png"),
RichText(
textAlign: TextAlign.center,
text: TextSpan(children: <TextSpan>[
TextSpan(
text: Constants.textIntro,
style: TextStyle(
color: Constants.kBlackColor,
fontWeight: FontWeight.bold,
fontSize: 30.0,
)),
TextSpan(
text: Constants.textIntroDesc1,
style: TextStyle(
color: Constants.kDarkBlueColor,
fontWeight: FontWeight.bold,
fontSize: 30.0)),
TextSpan(
text: Constants.textIntroDesc2,
style: TextStyle(
color: Constants.kBlackColor,
fontWeight: FontWeight.bold,
fontSize: 30.0)),
])),
SizedBox(height: size.height * 0.01),
Text(
Constants.textSmallSignUp,
style: TextStyle(color: Constants.kDarkGreyColor),
),
SizedBox(height: size.height * 0.1),
SizedBox(
width: size.width * 0.8,
child: OutlinedButton(
onPressed: () {
result == null
? Navigator.pushNamed(
context, Constants.signInNavigate)
: Navigator.pushReplacementNamed(
context, Constants.homeNavigate);
},
child: Text(Constants.textStart),
style: ButtonStyle(
foregroundColor: WidgetStateProperty.all<Color>(
Constants.kPrimaryColor),
backgroundColor: WidgetStateProperty.all<Color>(
Constants.kBlackColor),
side: WidgetStateProperty.all<BorderSide>(
BorderSide.none)),
),
),
SizedBox(
width: size.width * 0.8,
child: OutlinedButton(
onPressed: () {},
child: Text(
Constants.textSignIn,
style: TextStyle(color: Constants.kBlackColor),
),
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
Constants.kGreyColor),
side: WidgetStateProperty.all<BorderSide>(
BorderSide.none)),
),
)
],
),
As you can see above, we use the RichText
, so we can have multiple text with different color according to the design. We also use the SizeBox
widget to add a space between different widgets. In the onPressed
of the Get Started button, we add the following:
1
2
3
4
5
6
7
onPressed: () {
result == null
? Navigator.pushNamed(
context, Constants.signInNavigate)
: Navigator.pushReplacementNamed(
context, Constants.homeNavigate);
},
This will check if current user is null, then navigate to the sign in page, else navigate to the home page and remove all routes from the stack. The above will give you the following screen:
Note: You can also check if a user is logged in or not by using a splash screen as can be seen here
Creating The Google Sign-in Page
Now create a different page called sign_in_page.dart
, and inside that page create a SignPage
class:
1
2
3
4
5
6
7
8
9
10
11
12
13
class SignInPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
OutlineInputBorder border = OutlineInputBorder(
borderSide: BorderSide(color: Constants.kBorderColor, width: 3.0));
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Constants.kPrimaryColor,
body: Center(
child:
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Image.asset("assets/images/sign-in.png"),
As you can see here, we create a Scaffold
widget, which will contain a Column
widget and that widget will contain a list of widgets. After adding all the other widgets inside the column (check code on Github), then we create a StatefulWidget
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class GoogleSignIn extends StatefulWidget {
GoogleSignIn({Key? key}) : super(key: key);
@override
_GoogleSignInState createState() => _GoogleSignInState();
}
class _GoogleSignInState extends State<GoogleSignIn> {
bool isLoading = false;
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return !isLoading? SizedBox(
width: size.width * 0.8,
child: OutlinedButton.icon(
icon: FaIcon(FontAwesomeIcons.google),
onPressed: () async {
setState(() {
isLoading = true;
});
FirebaseService service = new FirebaseService();
try {
await service.signInwithGoogle();
Navigator.pushNamedAndRemoveUntil(context, Constants.homeNavigate, (route) => false);
} catch(e){
if(e is FirebaseAuthException){
showMessage(e.message!);
}
}
setState(() {
isLoading = false;
});
},
label: Text(
Constants.textSignInGoogle,
style: TextStyle(
color: Constants.kBlackColor, fontWeight: FontWeight.bold),
),
style: ButtonStyle(
backgroundColor:
WidgetStateProperty.all<Color>(Constants.kGreyColor),
side: WidgetStateProperty.all<BorderSide>(BorderSide.none)),
),
) : CircularProgressIndicator();
}
The above StatefulWidget
will also be called from the Column
widget. Now as you can see above, inside the onPressed
we use setState
which will call the build()
method with the new value of Loading
. We also initialize the custom class FirebaseService()
and call signInWithGoogle()
which we will see in the next section.
The method signInwithGoogle()
is asynchronous, therefore we use async/await
to wait until it finishes. We also use it inside a try/catch
. If an error occurs, it will call the showMessage()
method which will show a dialog with the error:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void showMessage(String message) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error"),
content: Text(message),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
The above code will give you the following screen:
Performing Google Sign-in
In the previous section, we called the following method service.signInwithGoogle();
. First, create a folder called services
and inside of it create a class called FirebaseService
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class FirebaseService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = GoogleSignIn();
Future<String?> signInwithGoogle() async {
try {
final GoogleSignInAccount? googleSignInAccount =
await _googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount!.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
await _auth.signInWithCredential(credential);
} on FirebaseAuthException catch (e) {
print(e.message);
throw e;
}
}
Future<void> signOutFromGoogle() async{
await _googleSignIn.signOut();
await _auth.signOut();
}
}
As you can see we initialize both FirebaseAuth
and GoogleSignIn
which will allows you to authenticate Google users. So first you need to call _googleSignIn.signIn();
which returns a Future<GoogleSignInAccount?>
. The signIn()
method will start the interactive sign-in process in which you choose a dialog is shown and you choose a gmail account. Then we call googleSignInAccount!.authentication
which will retrieve the authentication token after sign in.
Now we have access to both the accessToken
and the idToken
, therefore we create a new GoogleAuthCredential
from the provided tokens. Then, we finally call _auth.signInWithCredential(credential);
which will sign in the user to Firebase with the given Google credentials. The reason we sign in to Firebase, so later when we call currentUser
we can access the email/name/and photo url of the user.
Also, in the above class we have the method signOutFromGoogle()
, which we will use to log out from both Firebase and Google sign-in.
Therefore, when we click the onPressed
property discussed in previous section, we would get the following screen:
Retrieve User Information
Now, we create another file called home_page.dart
which will contain the HomePage
class. Inside the class, we need to retrieve the current user by calling:
1
User? user = FirebaseAuth.instance.currentUser;
Then inside the Column
widget, we add the following widgets:
1
2
3
4
5
6
7
8
9
10
11
12
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(user!.email!),
Text(user!.displayName!),
CircleAvatar(
backgroundImage: NetworkImage(user!.photoURL!),
radius: 20,
)
],
)))
Here we retrieve the email, name and the photo url of the user. The above code will give us the following screen:
As you can see from the image, the app bar will also contain a logout button in which we will call the method service.signOutFromGoogle();
which is inside the FirebaseService
class.
I hope you enjoyed reading this flutter tutorial, please feel free to leave any comments or feedback on this post!