Firebase authentication & Google sign in using Flutter

Sep 30, 2019

Firebase Authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users to your app. It supports authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook and Twitter, and more.

In this article, I will be showing how to set up and implement Google sign in using Firebase authentication.

There has been a lot of changes in Flutter (after the release of Flutter 1.7) as well as in the Firebase Google sign in.

So, if you want to build an app using Firebase authentication, I would suggest that you take a look at this article.

So, let’s get started.

Flutter 1.7

With the introduction of Flutter 1.7, the most frustrating thing – fixing AndriodX incompatibilities – has been taken care of. Now, you can just create a new project using the --androidx flag to ensure the generated project targets the new support library. I will be using this later in the article while creating the project.

A few other important things that have been included in Flutter 1.7, are the support for Android app bundle and 64-bit Android app support, which is very helpful if you want to publish your app in Play Store. Until now, we had to change the Flutter branch from stable to beta in order to generate the Android App Bundle, but now it is available in the stable branch.

Google sign in

There are a few new steps you have to do in order to use Google sign in in your app. Without completing all these steps, if you try to use Google sign in, your app will just crash. I will show you how to set up Google sign in using Firebase from scratch.

App screenshots

Our final app will look like this:

Final design
Final design

Implementation

Before diving into the real coding part, let’s see what are the plugins and assets required in this project.

Plugins

The plugins needed for this project are:

  1. firebase_auth (for using Firebase authentication)
  2. google_sign_in (for the implementation of Google sign in)

Assets

Only one image is needed for this project which we will be requiring while designing the Google sign in button in our app.

You can get the image from here.

Creating the project

Let’s create a new Flutter project with AndroidX compatibility.

  1. Open Terminal or use the terminal in your code editor.
  2. Navigate to the folder where you want to create the project and use the following command:

    flutter create --androidx sign_in_flutter
    

    This will add AndroidX compatibility to the project automatically.

    But for using the --androidx flag, you need to be on the latest version of Flutter on the stable branch.

  3. To check which branch you are currently on, use the command:

    flutter channel
    

    The channel marked with an asterisk (*) is your current branch.

  4. To change the branch to stable, use the following command:

    flutter channel stable
    
  5. Then upgrade, using the command:

    flutter upgrade
    

Importing packages

First of all, add the packages in pubspec.yaml file and save it to run flutter packages get .

pubspec.yaml
pubspec.yaml

Also, create a new folder called assets in your project directory and insert the image (google_logo.png) that you downloaded.

Now, add the assets folder in your pubspec.yaml file.

pubspec.yaml
pubspec.yaml

Coding part

Go to the main.dart file and import the firebase_auth package.

import 'package:firebase_auth/firebase_auth.dart';
main.dart
main.dart

Now, run the app on your device to verify whether the AndroidX support is working properly. If you are getting a BUILD FAILED error due to AndroidX incompatibilities, then it is because your AndroidX support libraries are not being imported properly.

You should always do this step first so that you don’t face any trouble later due to AndroidX.

Building the UI

After verifying, just delete everything from the main.dart file and paste the boilerplate code given below.

import 'package:flutter/material.dart';

import 'login_page.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Login',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LoginPage(),
    );
  }
}

Let’s build the UI for LoginPage.

Create a new dart file called login_page.dart inside the lib folder.

The LoginPage design would look like this:

Login page design
Login page design

This screen just contains two main components:

  1. Flutter Logo
  2. Sign in with Google button

LoginPage should be a Stateful Widget because we will be making some changes to the UI later which will need the widgets to be redrawn.

The code for the LoginPage UI:

import 'package:flutter/material.dart';

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Colors.white,
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              FlutterLogo(size: 150),
              SizedBox(height: 50),
              _signInButton(),
            ],
          ),
        ),
      ),
    );
  }

  Widget _signInButton() {}
}

Now, we have to design the Sign in with Google button inside the function _signInButton.

The code for button design:

Widget _signInButton() {
    return OutlineButton(
      splashColor: Colors.grey,
      onPressed: () {},
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)),
      highlightElevation: 0,
      borderSide: BorderSide(color: Colors.grey),
      child: Padding(
        padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Image(image: AssetImage("assets/google_logo.png"), height: 35.0),
            Padding(
              padding: const EdgeInsets.only(left: 10),
              child: Text(
                'Sign in with Google',
                style: TextStyle(
                  fontSize: 20,
                  color: Colors.grey,
                ),
              ),
            )
          ],
        ),
      ),
    );
  }

We will fill up the onPressed method later.

Firebase authentication

Create a new dart file sign_in.dart where we will set up the Firebase authentication and google sign in.

First of all, import the two packages:

  1. firebase_auth
  2. google_sign_in
import 'package:firebase_auth/firebase_auth.dart';

import 'package:google_sign_in/google_sign_in.dart';

Now, we will need to create an instance of FirebaseAuth & GoogleSignIn .

final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();

Then, create two methods:

  1. signInWithGoogle
  2. signOutGoogle
Future<String> signInWithGoogle() async {}
void signOutGoogle() async{}

In the signInWithGoogle method, we have to use the Google sign in data to authenticate a FirebaseUser and then return that user.

In the signOutGoogle method, we have to just sign out of the current Google account.

The final methods will look like this:

import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();

Future<String> signInWithGoogle() async {
  final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
  final GoogleSignInAuthentication googleSignInAuthentication =
      await googleSignInAccount.authentication;

  final AuthCredential credential = GoogleAuthProvider.getCredential(
    accessToken: googleSignInAuthentication.accessToken,
    idToken: googleSignInAuthentication.idToken,
  );

  final AuthResult authResult = await _auth.signInWithCredential(credential);
  final FirebaseUser user = authResult.user;

  assert(!user.isAnonymous);
  assert(await user.getIdToken() != null);

  final FirebaseUser currentUser = await _auth.currentUser();
  assert(user.uid == currentUser.uid);

  return 'signInWithGoogle succeeded: $user';
}

void signOutGoogle() async{
  await googleSignIn.signOut();

  print("User Sign Out");
}

Now, we have to update the onPressed method of _signInButton inside LoginPage class, so that this signInWithGoogle method gets called.

...

onPressed: () {
    signInWithGoogle().whenComplete(() {
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) {
            return FirstScreen();
          },
        ),
      );
    });
  },

...

If the Google sign in is successful, then we will be taken to the FirstScreen which I will be implementing next.

For now, we will just add a Container with a light blue background in the FirstScreen for testing the Google sign in.

Test UI for FirstScreen:

import 'package:flutter/material.dart';

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(color: Colors.blue[100]),
    );
  }
}

Set up Firebase project

To create a new Firebase project, you will need to go here.

  1. Click on Add project to create a new project.

    Add Project
    Add Project
  2. Now, you have to enter a Project name and check the two checkboxes below. Then, click on Create project.

    Create Project
    Create Project
  3. Now, wait for the creation to complete and click on Continue.

    Creating
    Creating
  4. This will lead you to the Project Overview. From here you will be able to add Firebase to your Flutter project.

    Project Overview
    Project Overview

Android setup

First, we will set up Firebase for the Android side.

  1. Click on the Android icon.
  2. In this form, you have to first enter the Android package name. You can get the Android package name form your project directoryandroidappsrcAndroidManifest.xml. On the second line, there will be your package name. Just copy & paste it in the form.
  3. Then, you have to enter an App nickname which is optional (if you do not enter, then an auto-generated app name would be used).

    Android Setup
    Android Setup
  4. Now, you have to enter the SHA-1 hash. Just hover over the help (?) and click on See this page. This will take you to Authenticating Your Client page.

    Authenticating Your Client
    Authenticating Your Client

    From here you will get the command to generate the SHA-1 hash. Paste this in your IDE terminal to get the SHA-1 hash. Just copy & paste this in the form.

  5. Click on Register App.

    Register App
    Register App
  6. This will take you to the second step from where you can download the google-services.json file and just drag & drop it in your project directoryandroidapp. Then, click on Next.

    Add Firebase to Android app
    Add Firebase to Android app
  7. In the third step, just follow the instructions and add the code snippets accordingly in the desired position. Then, click on Next.

    Add Firebase SDK
    Add Firebase SDK
  8. Finally, to complete the fourth step, just run the app on your device and attempt a Google sign in from the device for the first time.

    Make sure that the internet connection of the device is turned on.

    Run app to verify
    Run app to verify
  9. After completing this step, you will see that the Continue to Console button gets enabled. Click on it to go to the console of the app.

    Successfully added Firebase
    Successfully added Firebase

    But you will find that the app has paused and you will see in your IDE that an Exception has occurred. Don’t worry, this exception occurred because we have not enabled Google as a sign-in provider in our Firebase console.

    IDE error
    IDE error

Let’s fix this issue.

  1. Go to Authentication PageUsers tab and click on Set up sign-in method.

    Set up sign-in
    Set up sign-in
  2. In the sign-in providers page, edit Google sign-in.

  3. Here, you have to enter the project name and support email, and Enable this by clicking the toggle on the top-right corner. Then, click on Save.

    Google sign-in edit page
    Google sign-in edit page
  4. So, now the Google sign in setup is complete. Run the app on the device again.

    But to your surprise, you will again see that the same Exception occurs. Now, this gets interesting, this occurs because you have not filled up the OAuth consent form. This is the new change in Firebase that I was talking about from the beginning and it is just driving developers crazy because I have not found them properly documented anywhere.

Let’s see how to fix this issue.

  1. First of all, go to this page here.
  2. Make sure you are signed in with the same account with which you have created the Firebase project.
  3. Also, make sure that on the top-left corner your project is selected for which you are filling this consent.

    Consent form
    Consent form
  4. Go to CredentialsOAuth consent screen tab and start filling the form.

  5. Enter Application name, Application logo & Support email.

    In the Application logo, you can just enter any image if you are not making this for production, otherwise, enter the real app icon that you are using for your application.

    Consent form
    Consent form
  6. Then, scroll down and fill the Application Homepage link, Application Privacy Policy link and Application Terms of Services link.

    In all these places, you have to enter the same link starting with http://, then your app domain name which I have marked with green below.

    Consent form
    Consent form
  7. Click on Save.

So, this completes the whole setup process for Sign in with Google using Firebase for Android. Now, again run the app on your device and attempt Google sign in. You will find that it suddenly started working without any exception.

Note: You should follow all the above steps properly without skipping any of them to get a functional Firebase app.

iOS setup

If you have skipped the Android setup and arrived directly at the iOS setup, then make sure that you check out how to fill the consent form above; otherwise, you might face some error later.

  1. Go to SettingsGeneral tab.

    Firebase Settings
    Firebase Settings
  2. Scroll down and click on Add app.

    Firebase Add App
    Firebase Add App
  3. Now, select iOS.

    iOS setup
    iOS setup
  4. Enter the iOS bundle ID and App nickname. Then, click on Register app.

    iOS setup
    iOS setup

    You can get the Bundle ID inside iosRunner.xcodeprojproject.pbxproj by searching for “PRODUCT_BUNDLE_IDENTIFIER” using Ctrl + F.

    iOS Bundle ID
    iOS Bundle ID
  5. In the second step, you have to download the GoogleService-Info.plist file.

    iOS setup
    iOS setup
  6. Open the ios folder of the project directory in Xcode by right-clicking and selecting Open in Xcode.

    Xcode
    Xcode
  7. Now, drag & drop the file that you downloaded in the Runner subfolder. A dialog box would appear, make sure that in “Add to targets” Runner is selected and click on Finish.

    Xcode
    Xcode
  8. Close Xcode. Then, in your IDE go to iosRunnerInfo.plist. Here, you have to add something.

    Open this google_sign_in flutter package page. Inside iOS integration you will find a code snippet. Just copy & paste it inside the Info.plist file and save it, like this:

    Info.plist
    Info.plist
  9. Here, you will see that there is a TODO written to replace the value within the string tag with REVERSED_CLIENT_ID. You will find the REVERSED_CLIENT_ID in the file GoogleService-Info.plist.

    GoogleService-Info.plist
    GoogleService-Info.plist
  10. Go to the third step, you do not need to follow this step because we are using the Flutter Firebase plugin which takes care of all these things. You will only need to do this step if you are building a standalone iOS app. Click on Next.

    Add Firebase SDK
    Add Firebase SDK
  11. In the fourth step, you also do not need to follow anything. Just click on Next.

    Add code
    Add code
  12. Now, run the app on your iOS device and attempt a Google sign in to complete the fifth step.

    Verify
    Verify
  13. After completing this step, click on Continue to console.

    Continue to console
    Continue to console

So finally, this completes the whole setup process for Sign in with Google using Firebase for both Android and iOS. Just follow the above steps properly and you should get past the setup pretty quickly.

Improving the UI

Let’s complete our FirstScreen UI. The final UI will look like this:

FirstScreen UI
FirstScreen UI

I will not go deep into this UI coding part. You have to include some lines of code within the sign_in.dart file to retrieve the image URL, name & email of the user.

You have to add the following in sign_in.dart:

// Add these three variables to store the info
// retrieved from the FirebaseUser
String name;
String email;
String imageUrl;

...

// Add the following lines of code inside the
// signInWithGoogle method
final FirebaseUser user =
await _auth.signInWithCredential(credential);

// Add the following lines after getting the user
// Checking if email and name is null
assert(user.email != null);
assert(user.displayName != null);
assert(user.photoUrl != null);

name = user.displayName;
email = user.email;
imageUrl = user.photoUrl;

// Only taking the first part of the name, i.e., First Name
if (name.contains(" ")) {
   name = name.substring(0, name.indexOf(" "));
}

The full UI code for FirstScreen is given below:

import 'package:flutter/material.dart';
import 'package:sign_in_flutter/login_page.dart';
import 'package:sign_in_flutter/sign_in.dart';

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topRight,
            end: Alignment.bottomLeft,
            colors: [Colors.blue[100], Colors.blue[400]],
          ),
        ),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              CircleAvatar(
                backgroundImage: NetworkImage(
                  imageUrl,
                ),
                radius: 60,
                backgroundColor: Colors.transparent,
              ),
              SizedBox(height: 40),
              Text(
                'NAME',
                style: TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.bold,
                    color: Colors.black54),
              ),
              Text(
                name,
                style: TextStyle(
                    fontSize: 25,
                    color: Colors.deepPurple,
                    fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 20),
              Text(
                'EMAIL',
                style: TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.bold,
                    color: Colors.black54),
              ),
              Text(
                email,
                style: TextStyle(
                    fontSize: 25,
                    color: Colors.deepPurple,
                    fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 40),
              RaisedButton(
                onPressed: () {
                  signOutGoogle();
                  Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) {return LoginPage();}), ModalRoute.withName('/'));
                },
                color: Colors.deepPurple,
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Text(
                    'Sign Out',
                    style: TextStyle(fontSize: 25, color: Colors.white),
                  ),
                ),
                elevation: 5,
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(40)),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Configuring Firebase on Codemagic

In our local configuration, we have downloaded the google-services.json & GoogleService-Info.plist files and kept it in the android & ios directory of the Flutter app respectively. These files contain sensitive data, like the API key and information about the app. These files shouldn’t be checked in to version control or exposed to the world. We have to take certain steps to secure our sensitive data.

Ignore the files in version-control system

First of all, we have to add the two private files, google-services.json (present in the folder android/app/) & GoogleService-Info.plist (present in the folder ios/Runner/) in the .gitignore file, to avoid it being checked in to the version control, such as Git.

Once, you have add the two files in .gitignore , then just commit them.

Encode the files

At this stage, we have ensured that we won’t commit the sensitive files to version control system. However, the continuous integration server will expect to find this file in order to successfully build the Flutter app.

To encode the files, run the following two commands from your terminal in the project directory:

openssl base64 -in android/app/google-services.json
openssl base64 -in ios/Runner/GoogleService-Info.plist

This command will print the encoded content which we need to paste in the Codemagic environment variable.

Adding environment variable in Codemagic

  1. Log in in to your Codemagic account using GitHub, GitLab or Bitbucket.
  2. In your Applications list, search for your project.
  3. Then click on the Settings button to go to the settings of the app.

    Codemagic Applications
    Codemagic Applications
  4. Expand the Environment variables tab.

    Environment variables
    Environment variables
  5. Add the two encoded content strings generated from the terminal as:

    • ANDROID_FIREBASE_JSON (Variable name) -> encoded string from google-services.json (Variable value)
    • IOS_FIREBASE_JSON (Variable name) -> encoded string from GoogleService-Info.plist (Variable value)

    Don’t forget to check Secure checkbox to ensure that the values won’t be displayed in the build logs.

  6. Click on Save.

    Environment variables
    Environment variables

Adding a pre-build script

  1. In the Codemagic app settings, before the Build stage, click on the + button.

    Pre-build
    Pre-build
  2. Add the following Pre-build script.

    #!/bin/sh
    echo $ANDROID_FIREBASE_JSON | base64 --decode > $FCI_BUILD_DIR/android/app/google-services.json
    
    echo "Listing android Directory to confirm the google-services.json is there! "
    ls android/app/
    
    echo $IOS_FIREBASE_JSON | base64 --decode > $FCI_BUILD_DIR/ios/Runner/GoogleService-Info.plist
    
    echo "\nListing iOS Directory to confirm the GoogleService-Info.plist is there! "
    ls ios/Runner/
    
  3. Click on Save

    Pre-build script
    Pre-build script

Start building

Click on the Start new build button to start the build process.

Start build
Start build

While the build process is running, you can click on the Pre-build tab to see the output of the code we added to the pre-build script. You can see that both files, google-services.json & GoogleService-Info.plist, are present, though we had not added them to the version-control system. They are generated from the encoded strings.

Pre-build tab
Pre-build tab

After the build gets finished successfully, we can also download the Android apk and iOS app to test it on a device.

Build successful
Build successful

Conclusion

Our Login Demo app is complete and we have successfully tested it using Codemagic by setting up Firebase with the encoded files. I hope that you have received some useful information from this article.

You can find the GitHub repo for this project in this link.


This article is written by Souvik Biswas. He is a passionate Mobile App Developer (Android and Flutter) and has worked on a number of mobile apps throughout his journey. Loves open source contribution on GitHub. He is currently pursuing a B.Tech degree in Computer Science and Engineering from Indian Institute of Information Technology Kalyani. He also writes Flutter articles on Medium - Flutter Community.

Codemagic CI for Flutter