FlutterアプリにFirebaseを使ってGoogle Sign-in機能を実装する


Published on April 05, 2020

FlutterアプリにFirebaseを使って認証機能を実装します。今回はGoogle認証。

参考記事: Flutter: Implementing Google Sign In

記事執筆時の環境

$ flutter --version
Flutter 1.12.13+hotfix.9 • channel stable •
https://github.com/flutter/flutter.git
Framework • revision f139b11009 (5 days ago) • 2020-03-30 13:57:30 -0700
Engine • revision af51afceb8
Tools • Dart 2.7.2

Flutterプロジェクトを作成


VS CodeでFlutter&Dartの拡張機能を入れている人は下記でプロジェクトを作成できます。

⌘ + ⇧ + pFlutter: New Projectを実行します。

flutter new project

Project名は、flutter_sample_google_signinとしましょう。


Google Sign-in pluginの追加


pubspec.yamlに追記します。

pubspec.yaml
dependencies:
  // ... others
  google_sign_in: ^4.4.0


firebase_auth pluginの追加


pubspec.yamlに追記します。

pubspec.yaml
dependencies:
  // ... others
  firebase_auth: ^0.15.5+3


Android用セットアップ


[project]/android/build.gradleに追記します。

android/build.gradle
dependencies {
    // ... others

    // google services classpathを追加
    classpath 'com.google.gms:google-services:4.3.3'
}

[project]/android/app/build.gradleに追記します。

android/app/build.gradle
// ファイル末尾に追加
apply plugin: 'com.google.gms.google-services'

※ Androidエミュレーターでdebugする際は、Google Play付のデバイスを使用しましょう。


Firebase設定


ここでbuildしたらエラーになります。まだ、Firebaseのプロジェクト作って無いですからね。

Firebaseプロジェクト作成

プロジェクトを追加を押して新しくFirebaseプロジェクトを作成します。 new firebase project

Android設定

AndroidアイコンをクリックしてAndroidの設定を追加します。

firebase initial

アプリ情報を登録します。 firebase setting android

デバッグ用の署名証明書 SHA-1取得

手順はこちら

Terminal
$ keytool -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore

キーストアのパスワードを入力してください:と表示されると思います。 変更していない人はandroidとタイプしEnterを押しましょう。

表示されたSHA-1の値をコピぺしましょう。

google-services.jsonを配置

ダウンロードしたgoogle-services.json[project]/android/app/フォルダへ配置します。

これでビルドは通るはずです。


google signin機能を有効にする


firebaseのプロジェクトを開き、Authenticationの設定を行います。

firebase-add-signin-method

ログイン方法を設定をクリックし、Googleを有効化します。

firebase-add-signin-method-google-enabled

iOS設定

firebaseのプロジェクトページにて、アプリを追加をクリック

firebase-add-newapp

必要な情報を入力して、アプリを登録します。

※iOS バンドル IDに関しては、プロジェクト内でPRODUCT_BUNDLE_IDENTIFIERで検索するとわかります。

firebase-setting-ios

次の画面で、GoogleService-Info.plistをダウンロードします。

iOSフォルダを右クリック->Open in XcodeでXcodeを開きます。

open-xcode

ダウンロードしたGoogleService-Info.plistRunnerフォルダにコピーします。

add-googleservice-Info

https://pub.dev/packages/googlesignin#ios-integration にあるコードを

ios/Runner/Info.plistにコピペします。

ios/Runner/Info.plist
<key>CFBundleURLTypes</key>
<array>
	<dict>
		<key>CFBundleTypeRole</key>
		<string>Editor</string>
		<key>CFBundleURLSchemes</key>
		<array>
			<!-- TODO Replace this value: -->
			<!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->
			<string>com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn</string>
		</array>
	</dict>
</array>

<!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->と書かれている部分を、

ダウンロードしたGoogleService-Info.plistに記載された値に変更してください。

これで設定完了です。


UIの実装


googleのロゴ取得

ここからアセットをダウンロードして、web用のvectorファイルを開き適当なサイズで作成しましょう。

ログインにおけるブランドの取り扱いガイドライン

assetsフォルダをプロジェクトルートフォルダに作成し、google_logo.pngとしてロゴファイルを配置します。

読み込めるようにpubspec.yamlに追記します。

pubspec.yaml
  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/

main.dart

main.dartを全消しして、下記に書き換えます。

lib/main.dart
import 'package:flutter/material.dart';

import 'login_page.dart';

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

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

login_page.dart

loginページです。

lib/login_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_sample_google_signin/sign_in.dart';
import 'first_screen.dart';

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

class _LoginPageState extends State<LoginPage> {
  
  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() {
    return OutlineButton(
      splashColor: Colors.grey,
      onPressed: () {
        signInWithGoogle().whenComplete(() {
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (context) {
                return FirstScreen();
              },
            ),
          );
        });
      },
      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,
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

sign_in.dart

lib/sign_in.dart
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();

String name;
String email;
String imageUrl;



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;

    // 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(" "));
  }

  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");
}

first_screen.dart

lib/first_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_sample_google_signin/login_page.dart';
import 'package:flutter_sample_google_signin/sign_in.dart';

class FirstScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topRight,
            end: Alignment.bottomLeft,
            colors: [Colors.orange[50], Colors.orange[100]],
          ),
        ),
        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.black,
                    fontWeight: FontWeight.normal),
              ),
              SizedBox(height: 20),
              Text(
                'EMAIL',
                style: TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.bold,
                    color: Colors.black54),
              ),
              Text(
                email,
                style: TextStyle(
                    fontSize: 25,
                    color: Colors.black,
                    fontWeight: FontWeight.normal),
              ),
              SizedBox(height: 40),
              RaisedButton(
                onPressed: () {
                  signOutGoogle();
                  Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) {return LoginPage();}), ModalRoute.withName('/'));
                },
                color: Colors.red,
                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)),
              )
            ],
          ),
        ),
      ),
    );
  }
}

結果

Login前 Login後
before-login after-login

If you like it, share it!