はじめに

近年ではモバイル系のクロスプラットフォーム開発のデファクトスタンダードとなっているFlutterですが、自分でも技術獲得をするためにアプリ開発をしていました。

なので、その際に学んだ知識についてブログにまとめていこうと思います。

 

  完成イメージ

今回は、タイトルにある通りFirebase Authenticationを利用してGoogle認証ログイン機能の実装について紹介します。

動作としては以下のようなイメージになります。

右矢印右矢印

 

  pubspec.yamlにパッケージを追加

pubspec.yamlに以下の3つのパッケージを追加してflutter pub getします。


dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  firebase_core: ^1.10.0
  firebase_auth: ^3.2.0
  google_sign_in: ^5.2.1

  gradleにパスを追加(Androidの場合)

Androidの場合は、以下のように2つのgradleスクリプトを修正します。

  • パッケージのbuild.gradle

buildscript {
    ...
    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.3.13'
    }
}

  • appのbuild.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

 

  Firebase Authenticationの設定

Firebase consoleでのAuthenticationのGoogle認証設定は以下のページでまとめているので参照して下さい。

 

 

設定が終わったら、Forebase consoleのプロジェクトの概要→プロジェクトの設定に進みgoogle-services.jsonをダウンロードします。

ダウンロードしたjsonファイルをAndroidプロジェクトのルートフォルダであるandroid/appのフォルダに配置します。

 

  main.dartに初期化処理を追加

まずは、main.dartに以下のようにFirebaseの初期化処理を追加します。


import 'package:firebase_core/firebase_core.dart';
import 'package:growing_sake/firebase_google_auth.dart';

void main() async{
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const GrowingSakeApp());
}

 

  Google認証ログイン画面の実装

完成イメージにあるような画面の実装は以下のようになります。

認証処理で特に大事な部分についてを赤字にしています。


import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:growing_sake/main.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

///
/// FirebaseでのGoogle認証によるログイン
///
class FirebaseGoogleAuth extends StatefulHookConsumerWidget {
  const FirebaseGoogleAuth({Key? key}) : super(key: key);

  @override
  ConsumerState<FirebaseGoogleAuth> createState() => _FirebaseGoogleAuthState();
}

class _FirebaseGoogleAuthState extends ConsumerState<FirebaseGoogleAuth> {

  // Google 認証
  final _google_signin  = GoogleSignIn(scopes: [
    'email',
    'https://www.googleapis.com/auth/contacts.readonly',
  ]);
  late GoogleSignInAccount googleUser;
  late GoogleSignInAuthentication googleAuth;
  late AuthCredential credential;

  // Firebase 認証
  final _auth = FirebaseAuth.instance;
  late UserCredential result;
  User? user;

  ///
  /// ユーザー画像を取得する
  ///
  ImageProvider getUserImage(String? userImage) {
    if (userImage != null && userImage != "") {
      return NetworkImage(userImage);
    } else {
      return const AssetImage("images/account_black.png");
    }
  }

  @override
  Widget build(BuildContext context) {
    final uid = ref.watch(uidProvider);
    user = _auth.currentUser;

    String? userImage;
    String userName;
    String loginState;
    bool loginButtonEnable;
    if (uid.compareTo("") == 0) {
      userImage = "";
      userName = "";
      loginState = 'ログアウト中';
      loginButtonEnable = true;
    } else {
      userImage = user?.photoURL;
      userName = user?.displayName ?? "";
      loginState = 'ログイン中';
      loginButtonEnable = false;
    }

    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.fromLTRB(8, 4, 8, 4),
              child: Container(
                color: const Color(0xfff0f0f0),
                padding: const EdgeInsets.fromLTRB(8, 16, 8, 16),
                child: Column(
                  children: [
                    ///
                    /// ログイン状態の場合はアイコンも表示する
                    ///
                    Container(
                      width: 100,
                      height: 100,
                      padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        image: DecorationImage(
                          fit: BoxFit.fill,
                          image: getUserImage(userImage),
                        ),
                      ),
                    ),

                    ///
                    /// ログイン状態の場合はユーザー名も表示する
                    ///
                    Container(
                      padding: const EdgeInsets.all(8),
                      child: Text(userName == "" ? loginState : userName),
                    ),

                    ///
                    /// 認証ボタンを横に並べる
                    ///
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        ///
                        /// Google認証によるログイン処理ボタン
                        ///
                        ButtonTheme(
                          height: 60.0,
                          child: RaisedButton(
                            child: const Text('Google認証\nログイン',
                              style: TextStyle(fontWeight: FontWeight.bold),),
                            textColor: Colors.white,
                            color: loginButtonEnable ? Colors.lightGreen : Colors.grey,
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(10),
                            ),

                            onPressed: () async {
                              if (!loginButtonEnable) return;
                              // Google認証の部分
                              googleUser = (await _google_signin.signIn())!;
                              googleAuth = await googleUser.authentication;

                              credential = GoogleAuthProvider.credential(
                                accessToken: googleAuth.accessToken,
                                idToken: googleAuth.idToken,
                              );

                              // Google認証を通過した後、Firebase側にログイン ※emailが存在しなければ登録
                              try {
                                result = await _auth.signInWithCredential(credential);
                                user = result.user;
                                ref.read(uidProvider.notifier).state = user!.uid;

                                ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('ようこそ' + user!.displayName!)));
                              } catch (e) {
                                print(e);
                              }
                            }
                          ),
                        ),

                        ///
                        /// Google認証によるログアウト処理ボタン
                        ///
                        ButtonTheme(
                          height: 60.0,
                          child: RaisedButton(
                              child: const Text('Google認証\nログアウト',
                                style: TextStyle(fontWeight: FontWeight.bold),),
                              textColor: Colors.white,
                              color: loginButtonEnable ? Colors.grey : Colors.lightGreen,
                              shape: RoundedRectangleBorder(
                                borderRadius: BorderRadius.circular(10),
                              ),

                              onPressed: () {
                                if (loginButtonEnable) return;
                                _auth.signOut();
                                _google_signin.signOut();
                                ref.read(uidProvider.notifier).state = "";
                                ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('ログアウトしました')));
                              }
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

 

 

  さいごに

今回の内容は以上となります。

この実装をしたAndroidアプリはGooglePlayで公開されていますので是非参照してみて下さい!

 

 

また、ソースコードについてもGitHubで公開しています!