Authenticate with a Gmail Account in your Flutter apps using Firebase Authentication

Roman Jaquez
Flutter Community
Published in
8 min readJan 10, 2020

--

Make your users feel secure while using your apps, while providing the convenience of leveraging an existing Gmail account for authentication.

This tutorial shows what you can do at a bare minimum to provide basic authentication in your apps. I’m only focused on one of the many sign-in providers that Firebase offers — Google. In upcoming tutorials I’ll show how to authenticate with Email/Password, Phone, Facebook, Twitter, Github, Microsoft, etc.

We’ll be building a two-page app that allows users to log in using their Google Account. You can implement this in your apps to apply a layer of security and to pull their information associated with their Gmail account such as their display name and avatar image, and add a personal touch. It will look as follows:

Users can click on the “Login with Google” button, which will launch a web view where users can provide their Google credentials. Upon success, control is returned to the user and Flutter navigates the user to a second page, where they can view their avatar, name and the ability to log out. They can navigate back to the previous page, but if they click on the “Login with Google” again, the app knows they’re logged in already so it doesn’t prompt them again for credentials. Link to the Github Repo here.

Upfront Setup

Before anything, you have to set up a Firebase account and configure Firebase Authentication. First, configure Firebase for both IOS and Android by following these links:

Android
https://firebase.google.com/docs/android/setup

IOS
https://firebase.google.com/docs/ios/setup

YouTube Flutter Video
https://www.youtube.com/watch?v=DqJ_KjFzL9I

Once you’re all set setting them up (make sure you register both IOS and Android apps in your Firebase project as shown in the links above), you must enable the Firebase Authentication Google Sign-in Provider.

Go to your Firebase Account Console, select Authentication -> Sign-in method, and out of the available Sign-in providers, select Google.

Selecting the Sign-in Method in Firebase Console.

From the dialog that shows, toggle the Enable option, leave the default public-facing name for your project for now, select the project support email, then click Save.

The Sign-in Provider row in Firebase should look like this:

Now, at this point we’re done with the Firebase Console side of things, so let’s code!

Importing the Required Packages

Dependencies: Firebase Auth and Google Sign in Flutter Packages

Make sure you that in your pubspec.yaml you have the following dependencies:

dependencies:
firebase_core: ^0.5.0
firebase_auth: ^0.18.0+1
google_sign_in: ^4.5.3

flutter:
sdk: flutter

And in your main.dart, you import the following packages:

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

We’ll be creating two screens, one called LoginPageWidget and a WelcomeUserWidget. Start by setting up your main app and load the LoginPageWidget as the initial widget:

... (rest of the code omitted for brevity) ...void main() => runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginPageWidget()
));
class LoginPageWidget extends StatelessWidget {
... (rest of the code will be here)...
}

Let’s start by defining what the logic for our LoginPageWidget in regards to authentication:

GoogleSignIn _googleSignIn = GoogleSignIn();
FirebaseAuth _auth;
bool isUserSignedIn = false;

Notice we’ve created an instance of the GoogleSignIn and the FirebaseAuth. The _auth FirebaseAuth instance is the entrypoint of the Firebase Authentication, which hides the complexity of authenticating users from multiple sign-in providers, given third-party credentials (in our case, Google credentials).

The _googleSignIn GoogleSignIn instance allows us to authenticate Google Users (Firebase supports many other providers as mentioned before). Once GoogleSign in kicks in, it returns token/access pair and returns additional identity provider data.

I’ll also create a global property called isUserSignedIn — for the purpose of this tutorial so I can keep a reference whether the user is logged in for my LoginPageWidget.

In my initState() method, I’ll initialize the state as usual, and I’ll invoke a small method called initApp() that I’ll use to properly initialize my default Firebase app, as well as fetching an instance of the Firebase Authentication via FirebaseAuth, which requires an instance of the default app that we just initialized.

@override
void initState() {
super.initState();
initApp();
}
void initApp() async {
FirebaseApp defaultApp = await Firebase.initializeApp();
_auth = FirebaseAuth.instanceFor(app: defaultApp);
// immediately check whether the user is signed in
checkIfUserIsSignedIn();
}

Update: The above changes were introduced recently, where you have to initialize your Firebase app (after doing all the configuration required for IOS and Android via the google-services.json (Android) and the GoogleService-Info.plist (IOS). Make sure to grab my latest updated Github Repo here.

We’ll create a method called _handleSignIn() that will asynchronously handle the Google Sign-In process and return a Firebase User object upon success. A User (part of Firebase Auth) is nothing more than a wrapper around an already authenticated user, regardless of the third-party sign-in provider.

Future<User> _handleSignIn() async {   // hold the instance of the authenticated user
User user;
// flag to check whether we're signed in already
bool isSignedIn = await _googleSignIn.isSignedIn();
setState(() {
isUserSignedIn = userSignedIn;
});
if (isSignedIn) {
// if so, return the current user
user = _auth.currentUser;
}
else {
final GoogleSignInAccount googleUser =
await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
// get the credentials to (access / id token)
// to sign in via Firebase Authentication
final AuthCredential credential =
GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken
);
user = (await _auth.signInWithCredential(credential)).user;
userSignedIn = await _googleSignIn.isSignedIn();
setState(() {
isUserSignedIn = userSignedIn;
});
}

return user;
}

The above code is pretty self explanatory: Google Sign-In workflow is queried initially to see whether there’s an existing signed in user (_googleSignIn.isSignedIn()). If so, then we can grab it and move on. Otherwise we will initiate the sign-in workflow by calling the _googleSignIn.signIn() method, which pops up a web view in our Flutter app that handles the capturing of the user’s Google credentials in a secure manner, which returns a GoogleSignInAccount instance and we capture it in a local variable called googleUser. From the googleUser we extract the authentication pieces we need the accessToken and idToken to generate valid credentials (AuthCredential instance obtained via the GoogleAuthProvider.getCredential method invocation) to pass along to Firebase Authentication. Eventually we use these credentials and sign in using the GoogleSignInAuthentication’s signInWithCredential method, passing the credentials created, from which (upon a successful login workflow) we’ll get back the authenticated Google user, wrapped in a FirebaseUser object.

We’ll wrap the triggering of the _handleSignIn method inside another one call onGoogleSignIn, which awaits for the retrieval of the User model, and then passes it to the WelcomeUserWidget page, along with the instance of _googleSignIn. You can hold on to that single instance of the GoogleSignIn in any way you see fit — via a Singleton, InheritedWidget, BLoC pattern, etc. I decided to just pass it back and forth between pages for simplicity and for the sake of this tutorial. But holding on to this reference is key as it is used to retrieve the existing user as well as signing out.

void onGoogleSignIn(BuildContext context) async {
User user = await _handleSignIn();
var userSignedIn = Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
WelcomeUserWidget(user, _googleSignIn)));
setState(() {
isUserSignedIn = userSignedIn == null ? true : false;
});
}

We’ll get to the WelcomeUserWidget further down below. Let’s wrap up the LoginPageWidget. Let’s dissect the build method.

@override
Widget build(BuildContext context) {

return Scaffold(
body: Container(
padding: EdgeInsets.all(50),
child: Align(
alignment: Alignment.center,
child: FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)
),
onPressed: () {
onGoogleSignIn(context);
}
,
color: Colors.blueAccent,
child: Padding(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(
Icons.account_circle,
color: Colors.white),
SizedBox(width: 10),
Text(‘Login with Google’,
style: TextStyle(color: Colors.white))
],
)
)
)
)
)
);
}

All we’re doing in this method is just creating a FlatButton with an icon and some text, but in the onPressed method is where we trigger the onGoogleSignIn method, passing the context so we can perform the navigation to the WelcomeUserWidget page.

The WelcomeUserWidget, as shown below, captures the instance of the currently logged in user (User object provided by Firebase Auth), displays its photoUrl and displayName properties in an Image and Text widget respectively, as well as the ability to log them out using the _googleSignIn instance passed from the previous page. On the FlatButton’s onPressed method, we take care of signing the user out and immediately pop out the view to take them back.

class WelcomeUserWidget extends StatelessWidget {

GoogleSignIn _googleSignIn;
User _user;
WelcomeUserWidget(User user, GoogleSignIn signIn) {
_user = user;
_googleSignIn = signIn;

}
@override
Widget build(BuildContext context) {
.
.
.
ClipOval(
child: Image.network(
_user.photoURL,
width: 100,
height: 100,
fit: BoxFit.cover
)
),
.
.
.
Text(_user.displayName,
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25)),
.
.
.
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
onPressed: () {
_googleSignIn.signOut();
Navigator.pop(context);
}

),
.
.
.
}

Update: I converted the LoginPageWidget to a StatefulWidget so I can better capture the state of the user being logged in and reflect it on the login button by using a flag to change the color and the text label.

That’s it! With this basic implementation you can secure your applications while at the same time give them the personal touch your users deserve and expect. In upcoming tutorials I’ll tackle the rest of the third-party sign-in providers individually.

Here’s a Github repo with the working code for your reference. Don’t forget to replace the google-services.json in Android and GoogleService-Info.plist file in IOS in this Flutter project if you decide to fork or clone this repo.

Useful References

These are some links on things to keep in mind while debugging the Firebase Authentication setup, creation of your Firebase account and setting up Firebase for both IOS and Android in your respective projects:

https://stackoverflow.com/questions/54557479/flutter-and-google-sign-in-plugin-platformexceptionsign-in-failed-com-google
https://developers.google.com/android/guides/client-auth
https://developer.android.com/studio/build/multidex

Set Up Firebase for Android
https://firebase.google.com/docs/android/setup

Set up Firebase for IOS
https://firebase.google.com/docs/ios/setup

Youtube Flutter Video on Firebase
https://www.youtube.com/watch?v=DqJ_KjFzL9I

Happy Fluttering!

--

--

Roman Jaquez
Flutter Community

Flutter GDE / GDG Lawrence Lead Organizer / Follow me on Twitter @drcoderz — Subscribe to my YouTube Channel https://tinyurl.com/romanjustcodes