 Flutterの記事を整理し本にしました
 Flutterの記事を整理し本にしました  
- 本稿の記事を含む様々な記事を体系的に整理し本にまとめました
- 今後はこちらを最新化するため、最新情報はこちらをご確認ください
- 10万文字を超える超大作になっています(笑)
はじめに
- FlutterのFirebaseパッケージのメソッドとかがコロコロ変わる印象があり、先人方のコピペで動かない事があったので、最新版で動くものを整理しました。
まとめ
概要
FirebaseのAuthenticationの基本については、Firebaseの機能1-1:Authentication(メール)をご参照ください。
Firebase Authentication(以下Authentication)には、メールとパスワード以外にも、様々なSNSとの連携が実現できるようになっています。
本チャプターでは、一番代表的なGoogleアカウントとの連携の実装を解説します。
この認証方法を使えば、メールアドレスを登録することなく、Googleアカウントさえ持っていれば、認証を通ることができます。
開発者側は、ユーザのUIDがメールでもGoogle認証でも同じように払い出されますので、認証後は統一したロジックで処理を行うことができます。
事前準備
共通の設定
まずは、FirebaseでGoogleAuthを有効化します。
Authenticationから、プロバイダーのGoogleを有効にします。
プロジェクトの公開名はデフォルトでも構いませんが、認証時にユーザに表示されます。
Androidの設定
AndroidではSHA1のフィンガープリントが必要になります。
% keytool -list -v-alias androiddebugkey -keystore ~/.android/debug.keystore
キーストアのパスワードを入力してください:  
別名: androiddebugkey
作成日: 2020/08/09
エントリ・タイプ: PrivateKeyEntry
証明書チェーンの長さ: 1
証明書[1]:
所有者: C=US, O=Android, CN=Android Debug
発行者: C=US, O=Android, CN=Android Debug
シリアル番号: 1
有効期間の開始日: Sun Aug 09 19:24:48 PWT 2020終了日: Tue Aug 02 19:24:48 PWT 2050
証明書のフィンガプリント:
	 MD5: ********
	 SHA1: ********
	 SHA256: ********
	 署名アルゴリズム名: SHA1withRSA
	 バージョン: 1
まず、コマンドでSHA1のフィンガプリントを作成し、下記のアプリの設定部分に登録します。
パスワードは特にありませんので、そのままEnterを入れてください。
続いて、SHA1を設定した後は、再度google-services.jsonをダウンロードし配置します。
google-services.jsonの設定方法については、Firebase概要をご確認ください。
iOSの設定
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>{REVERSED_CLIENT_ID}</string>
        </array>
    </dict>
</array>
info.plistに設定を追加します。
下記イメージの通り、CFBundleURLSchemesにREVERSED_CLIENT_IDを設定します。
実装
ここ数年でメソッド名が変わったりしていますので、ご注意ください。
(FirebaseUserがUserに変更されたり、getCredentialがcredentialに変更されたりしています
本チャプターは下記のパッケージバージョンで動作確認をしています。
まず、必要なパッケージをインストールします。
必要に応じて、最新のバージョンをお使いください。
dependencies:
  firebase_core: ^1.0.4
  firebase_auth: ^1.1.1
  google_sign_in: ^5.0.2
続いて、検証に用いる画面のソースコードになります。
ログインのページとログアウトのページを準備し、それぞれの動作を確認していきます。
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:firebase_test/LoginPage.dart';
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: LoginPage(),
    );
  }
}
メイン画面は、LoginPageを呼び出すだけですが、Firebaseを初期化するために、main関数を非同期にした上で、初期化用のメソッドを呼んでいます。
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:firebase_test/LogoutPage.dart';
class LoginPage extends StatelessWidget {
  static final googleLogin = GoogleSignIn(scopes: [
    'email',
    'https://www.googleapis.com/auth/contacts.readonly',
  ]);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: Center(
        child: TextButton(
          onPressed: () async {
            GoogleSignInAccount signinAccount = await googleLogin.signIn();
            if (signinAccount == null) return;
            GoogleSignInAuthentication auth =
                await signinAccount.authentication;
            final GoogleAuthCredential credential =
                GoogleAuthProvider.credential(
              idToken: auth.idToken,
              accessToken: auth.accessToken,
            );
            User user =
                (await FirebaseAuth.instance.signInWithCredential(credential))
                    .user;
            if (user != null) {
              await Navigator.of(context).pushReplacement(
                MaterialPageRoute(builder: (context) {
                  return LogoutPage(user);
                }),
              );
            }
          },
          child: Text(
            'login',
            style: TextStyle(fontSize: 50),
          ),
        ),
      ),
    );
  }
}
続いて、ログイン画面です。
画面には、ボタンが1つあるだけで、ログインボタンを押すと認証を行います。
処理は、GoogleSignInでアカウントを取得したあとに、GoogleSignInAuthentication,とGoogleAuthCredentialで認証を行います。
Google認証が問題なく行われたあとに、この情報を使って、Firebaseに認証情報を登録します。
該当部分は、FirebaseAuth.instance.signInWithCredential(credential))になります。
返ってきたユーザ情報を持って、ログアウトページに遷移しています。
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:firebase_test/LoginPage.dart';
class LogoutPage extends StatelessWidget {
  User user;
  LogoutPage(this.user);
  static final googleLogin = GoogleSignIn(scopes: [
    'email',
    'https://www.googleapis.com/auth/contacts.readonly',
  ]);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text("Hello ${user.displayName}", style: TextStyle(fontSize: 50)),
            SizedBox(height: 20),
            TextButton(
              onPressed: () async {
                FirebaseAuth.instance.signOut();
                await googleLogin.signIn();
                await Navigator.of(context)
                    .pushReplacement(MaterialPageRoute(builder: (context) {
                  return LoginPage();
                }));
              },
              child: Text('Logout', style: TextStyle(fontSize: 50)),
            )
          ],
        ),
      ),
    );
  }
}
最後は、ログアウト画面です。
ログアウト画面は、認証されたuserのdisplayNameを使ってユーザ名を表示しています。
その下にログアウトのボタンを配置し、ボタンを押すとログアウトが行われます。
処理は、まずFirebaseAuth.instance.signOut();でFirebaseのログアウトを行います。
次にgoogleLogin.signIn();でGoogleのログアウトを行います。
最後に、ログインページに遷移しています。
動作イメージ
実際の動作イメージは下記のようになります。
初回のみ設定が必要となりますが、2回目からは問い合わせのみになるため、スムーズなログイン/ログアウトが行えます。
AndroidとiPhoneで表示される内容やステップが若干異なります。
また、2要素認証の有無によっても初回の動作が異なります。




