Help us understand the problem. What is going on with this article?

FlutterでFirebase Authenticationを使ったGoogleアカウントログインを実装してみた

はじめに

この記事は、Flutter #2 Advent Calendar 2019の7日目、Firebase #2 Advent Calendar 2019の7日目の記事です。

FlutterアプリFirebase Authenticationを使ったGoogleアカウントログインを実装してみたので、2つのAdvent Calendarに殴り込みをかけてみました(^ω^ ≡ ^ω^)

Introducing Firebase Authentication

筆者はFlutterもFirebaseも初心者なので、お手柔らかにお願いします(予防線)

Flutterプロジェクトの土台を作成する

以下、Flutterアプリの開発環境が整っている前提で話します。

[✓] Flutter (Channel stable, v1.9.1+hotfix.6, on Mac OS X 10.14.6 18G87, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.1)
[✓] Xcode - develop for iOS and macOS (Xcode 11.2.1)
[✓] Android Studio (version 3.4)
[✓] VS Code (version 1.40.2)
[✓] Connected device (1 available)

FlutterでHello worldを動かすまでの手順については、別途記事を書いております。Flutter未経験の方は合わせてご参考ください。

FlutterでHello worldを動かすまでの環境構築手順(iOS, Android)

とりあえず新規プロジェクトで土台を作成。
Flutterプロジェクト名は、flutter_firebase_authenticationとしました。

中身の実装については後述しますので、一旦寝かせといてください( ˘ω˘)スヤァ…

Firebaseプロジェクトを作成する

まずはFirebaseプロジェクトを作成します。Firebaseの公式ガイドに沿って進めていきましょう。
Flutter アプリに Firebase を追加する  |  Firebase

Firebase コンソールで [プロジェクトを作成] をクリック。
スクリーンショット 2019-12-06 22.33.36.png
すでにFirebaseプロジェクトが存在する場合は [プロジェクトを追加] をクリックしてください。
スクリーンショット 2019-12-07 00.35.27.png

既存のGCP(Google Cloud Platform)プロジェクトがある場合は、プルダウンからプロジェクトを選択できます。今回は新しいプロジェクト名を入力します。プロジェクト名は30文字以内となっていたので、flutter-firebase-authとしました。
スクリーンショット 2019-12-06 22.36.38.png

アプリにGoogle Analyticsを連携させることもできますが、今回は有効化せず [プロジェクトを作成] をクリック。
スクリーンショット 2019-12-06 22.38.44.png

しばらく待つと、Firebaseプロジェクトが作成されます。
スクリーンショット 2019-12-06 22.39.18.png
[続行] をクリックして終わります。

iOSアプリを構成する

続いてFlutterアプリがFirebaseプロジェクトに接続できるよう構成を行います。
今回は時間の都合上、iOSアプリの設定のみに留めます。Androidは自力で頑張ってください。

Firebaseコンソールにて、先ほど作成したFirebaseプロジェクトを開き、iOSアイコンをクリック。
スクリーンショット 2019-12-06 22.41.06.png
ウィザードに従って設定を行なっていきます。

アプリをFirebaseプロジェクトに登録する

アプリのバンドルIDを [iOS バンドル ID] に入力します。他項目は省略可ということで飛ばします。
スクリーンショット 2019-12-06 22.56.27.png

バンドルIDはXcodeでアプリを開き、最上位のRunnerを選択するなどすれば確認できます。
com.example.flutterFirebaseAuthenticationみたいな名前のやつです。
スクリーンショット 2019-12-07 00.52.09.png

バンドルIDを入力したら [アプリを登録] をクリックします。

GoogleService-Info.plistをアプリに追加

[GoogleService-Info.plist をダウンロード] をクリックして、Firebase iOS 構成ファイルを取得。ダウンロードしたGoogleService-Info.plistファイルを、Flutterアプリの Runner/Runner ディレクトリにコピーします。

スクリーンショット 2019-12-06 23.00.50.png
Firebaseコンソールのウィザードに戻り、[次へ] をクリックして残りの手順(pod initなど)をすべてスキップして完了します。

Firebase Authenticationの設定

最後にFirebase Authenticationの設定を行います。アプリに追加するプロダクトから [Authentication] を選択。
スクリーンショット 2019-12-06 23.27.48.png

ログイン方法で [Google] を選択し、サポートメールを入力。[有効にする] のトグルをオンの状態にして保存します。
スクリーンショット_2019-12-06_23_28_55_.jpg

これでFirebaseコンソール側の作業は完了です。

Flutterにプラグインを追加する

ここでようやくFutterプロジェクトに戻ります。pubspec.yamlファイルを開き、プラグインを追加しましょう。

今回はFirebase Authenticationを使ってGoogleアカウントのログインを実装するので、Firebase Core SDK用のFlutterFireプラグインを含め、3つのパッケージをインストールします。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^0.4.2+1
  firebase_auth: ^0.15.1
  google_sign_in: ^4.0.14

pubspec.yamlに追記してflutter pub getでインストール。

Info.plistにCFBundleURLTypes追加

iOSのgoogle_sign_inで必要な設定を追加します。

/ios/Runner/Info.plistCFBundleURLTypesを追加します。

Info.plist
<!-- Put me in the [my_project]/ios/Runner/Info.plist file -->
<!-- Google Sign-in Section -->
<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>
<!-- End of the Google Sign-in Section -->

CFBundleURLSchemesには、GoogleService-Info.plist内にあるREVERSED_CLIENT_IDキーの値をコピーして貼り付けます。

ログイン画面を作る

適当にログイン画面を作ります。詳細説明は省きますが、簡単な2つの画面を用意しました。

  • ログイン画面
  • プロフィール表示画面(ログイン後に遷移)

以下、画面の主要コード部分のみ抜粋。
エラー処理も雑なので、きちんと作るときは各自で何とかしてくれ(丸投げ)

importとか

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

ログイン画面

main.dart
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GoogleSignIn _googleSignIn = GoogleSignIn();
  final FirebaseAuth _auth = FirebaseAuth.instance;

  Future<FirebaseUser> _handleSignIn() async {
    GoogleSignInAccount googleCurrentUser = _googleSignIn.currentUser;
    try {
      if (googleCurrentUser == null) googleCurrentUser = await _googleSignIn.signInSilently();
      if (googleCurrentUser == null) googleCurrentUser = await _googleSignIn.signIn();
      if (googleCurrentUser == null) return null;

      GoogleSignInAuthentication googleAuth = await googleCurrentUser.authentication;
      final AuthCredential credential = GoogleAuthProvider.getCredential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );
      final FirebaseUser user = (await _auth.signInWithCredential(credential)).user;
      print("signed in " + user.displayName);

      return user;
    } catch (e) {
      print(e);
      return null;
    }
  }

  void transitionNextPage(FirebaseUser user) {
    if (user == null) return;

    Navigator.push(context, MaterialPageRoute(builder: (context) => 
      NextPage(userData: user)
    ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              RaisedButton(
                child: Text('Sign in Google'),
                onPressed: () {
                  _handleSignIn()
                    .then((FirebaseUser user) => 
                      transitionNextPage(user)
                  )
                  .catchError((e) => print(e));
                },
              ),
            ]
          ),
      ),
    );
  }
}

プロフィール表示画面

main.dart
class NextPage extends StatefulWidget {
  FirebaseUser userData;

  NextPage({Key key, this.userData}) : super(key: key);

  @override
  _NextPageState createState() => _NextPageState(userData);
}

class _NextPageState extends State<NextPage> {
  FirebaseUser userData;
  String name = "";
  String email;
  String photoUrl;
  final GoogleSignIn _googleSignIn = GoogleSignIn();

  _NextPageState(FirebaseUser userData) {
    this.userData = userData;
    this.name = userData.displayName;
    this.email = userData.email;
    this.photoUrl = userData.photoUrl;
  }

  Future<void> _handleSignOut() async {
    await FirebaseAuth.instance.signOut();
    try {
      await _googleSignIn.signOut();
    } catch (e) {
      print(e);
    }
    Navigator.pop(context);
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text("ユーザー情報表示"),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Image.network(this.photoUrl),
              Text(this.name,
                style: TextStyle(
                  fontSize: 24,
                ),
              ),
              Text(this.email,
                style: TextStyle(
                  fontSize: 24,
                ),
               ),
              RaisedButton(
                child: Text('Sign Out Google'),
                onPressed: () {
                   _handleSignOut().catchError((e) => print(e));
                },
              ),
            ]),
        ),
      );
  }

実際に動かしてみた

上記コードでアプリを起動するとログイン画面が表示。
スクリーンショット 2019-12-06 23.36.13.png

[Signe in Google] ボタンを押すと「Googleアカウントでログインするけどええか?」ダイアログが表示されるのでContinueする。
スクリーンショット 2019-12-06 23.36.29.png

Googleアカウントのログイン画面が出てくるので、アカウント名とパスワードを入力。
スクリーンショット_2019-12-06_23_36_37_.jpg

ログインが成功するとアカウント情報が表示される。
スクリーンショット 2019-12-06 23.37.15.png

やったぜ。

まとめ

というわけで、FlutterアプリのFirebase AuthenticationによるGoogleアカウントログインは、手順さえおさえれば簡単に実装できることがわかった。

本当はGitHubログインとTwitterログインの実装も盛り込みたかったのだが…アプリ登録まわりのフローがダル過ぎて挫折しました/(^o^)\
メールアドレスや電話番号によるSMS認証の実装は超簡単っぽい。

Sign in with AppleのFlutter対応も秒読みの気配。
[firebase_auth] Added Oauth for generic provider and solve auth for iOS devices by zariweyo · Pull Request #1526 · FirebaseExtended/flutterfire

FlutterとFirebaseはいいぞ

Flutterは手頃なアプリがサクッと作れるので超楽しいですね(^ω^)
他のアカウントログインも余裕ある時にまた挑戦したいと思います。
auth-providers.png
みんなもFlutterとFirebaseで最高のアプリを作ってくれよな!

Flutter - Beautiful native apps in record time
Firebase

現場からは以上です。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away