Categories:
Search articles

Search for articles

Sorry, but we couldn't find any matches...

But perhaps we can interest you in one of our more popular articles?

Google sign-in & Firebase authentication using Flutter

Sep 30, 2019

codemagic codemagic

This article was updated in May 2020.

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 Flutter app and implement Google sign-in using Firebase authentication.

This article is using Flutter 1.17.0 and all the latest versions of the plugins.

In this article you will learn how to:

So let's get started.

Google sign-in using Flutter

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. Let's see how to set up Google sign-in from scratch using Firebase.

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 Firebase authentication)
  2. google_sign_in (to implement Google sign-in)

Assets

Only one image is needed for this project. We will be requiring it when designing the Google sign-in button in our app.

You can get the image from here.

Creating a new Flutter project

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

  1. Open Terminal or use the terminal in your code editor.

  2. If necessary, you can check your current Flutter version with the command:

     flutter --version
    
  3. If you are on Flutter 1.12 or above, AndroidX compatibility is added to the project by default. Just navigate to the folder where you want to create the project and use the following command:

     flutter create sign_in_flutter
    

    If you are using an older Flutter version, you can add the --androidx flag.

Importing packages

  1. Add the following packages to your pubspec.yaml file:

    firebase_auth: ^0.16.0
    google_sign_in: ^4.4.4
    
    pubspec.yaml
    pubspec.yaml
  2. Save it to run flutter packages get.

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

Building the UI

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 contains the 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 the LoginPage class, so that the signInWithGoogle method gets called.

...

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

...

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

For now, we will just add a Container with a light blue background in the FirstScreen to test 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 a Firebase project

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

  1. Click Add project to create a new project.

  2. Enter a Project name and click Continue.

  3. Turn off Google Analytics for this project and click Create project.

  4. Now wait for the creation to complete and then click Continue.

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

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 find this from your project directoryandroidappsrcAndroidManifest.xml. On the second line, there will be your package name. Just copy & paste it in the form.

  3. Now you can choose an App nickname. It is optional and if you leave this field empty, an auto-generated app name will be used.

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

    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 Register App.

  6. This will take you to the second step. Download the google-services.json file and just drag & drop it in your project directoryandroidapp. Then, click Next.

  7. In the third step, just follow the instructions and add the code snippets in the desired position. Then, click Next.

  8. Click Continue to Console.

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

  10. In the sign-in providers page, edit Google sign-in.

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

    Google sign-in edit page
    Google sign-in edit page
  12. The Google sign-in setup is now complete. Run the app on your device to check if it is working properly.

Note: To get a functional Firebase app, make sure you follow all these steps properly without skipping any of them.

iOS setup

If you have skipped the Android setup and arrived directly at the iOS setup, then make sure that you check out the steps 9-12; otherwise, you might face errors later.

  1. Go to SettingsGeneral tab.

  2. Scroll down and click Add app.

  3. Now, select iOS.

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

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

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

  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 into the Runner subfolder. When a dialog box appears, make sure that Runner is selected in “Add to targets” box. Then click Finish.

  8. Close Xcode. 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. We reached the third step. You don't need to change anything in 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. So just click Next.

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

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

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

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

Codemagic Newsletter

Subscribe to our newsletter

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.

Thus, 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 and 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 the version control system

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

Once you have added the two files into .gitignore, commit the .gitignore file.

Encode the files

At this stage, we have ensured that we won't commit the sensitive files to the 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 into the Codemagic environment variable.

Adding the environment variable in Codemagic

  1. Log in in to your Codemagic account using GitHub, GitLab or Bitbucket.

  2. Find your project in your Applications list.

  3. Click the Settings button to go to the app settings.

  4. Expand the Environment variables tab.

  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 the Secure checkbox to ensure that the values won't be displayed in the build logs.

  6. Click Save.

Adding a pre-build script

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

    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 Save

    Pre-build script
    Pre-build script

Start building

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

While the build process is running, you can click 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 and GoogleService-Info.plist, are present, although we did not add them to the version-control system. They are generated from the encoded strings.

Pre-build tab
Pre-build tab

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

Build successful
Build successful

Conclusion

Our Log-in Demo app is now completed and we have successfully tested it with Codemagic by setting up Firebase with encoded files. I hope that you found some useful information from this article.

You can find the GitHub repo for this project HERE.


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.