【Flutter/Dart】Firebase Authenticationを使って認証アプリを作ろう

# 1.はじめに
新たに6つのAndroidStudio&Flutter&Firebase記事を書いている。以下は内容、
- 【Flutter/Dart】Firebaseを使おう
https://qiita.com/my_programming/private/7b3251701183ff59b97c
- **【Flutter/Dart】Firebase Authenticationを使って認証アプリを作ろう**
- 【Flutter/Dart】Firebase/Firestoreを使ったチャットアプリを作ろう
https://qiita.com/my_programming/private/ad0f7a5f7637a1a37842
- 【Flutter/Dart】Firebase/Firestoreを使ったToDoアプリを作ろう
https://qiita.com/my_programming/private/2da77f3a36bf120761c5
- 【Flutter/Dart】Firebase/HostingでWebアプリを外部公開しよう
https://qiita.com/my_programming/private/5950b7e27161dc9ec838
- 【Flutter/Dart】Firebase/Firestoreを使ったBlogアプリを作ろう
https://qiita.com/my_programming/private/6f8515dca825e55d0f07

Firebase AuthenticationはSDKを使用してモバイルアプリとの統合を非常に簡単にする事前構成済みのバックエンドサービスである。認証プロセスのためにバックエンドインフラを自作する必要はない。Firebaseは、Google、Facebook、GitHubなどの一般的なIDプロバイダーとの統合もサポートしている。Firebase AuthenticationをFlutterで実装する方法を説明する。

# 2.認証アプリを作ろう

FirebaseプロジェクトからAuthenticationを選択する。

<img width="400" alt="step2_1_1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/edbab3f9-024c-5206-9808-5dc04ccac273.png">

<br>

`始める`を押す。

<img width="400" alt="step2_1_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/11f35167-5475-07f8-473a-a55bd91c0a59.png">

<br>

今回は例としてメールアドレスで認証することとし`メール/パスワード`を選択する。

<img width="400" alt="step2_1_3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/2d6eaad4-868b-b739-f5cd-13c7ffef628b.png">

<br>

`メール/パスワード`を`有効`にする。

<img width="400" alt="step2_1_4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/7bdf839e-aa0c-29e4-ecd4-576f131cce3e.png">

<br>

以下の画面が表示されることを確認する。

<img width="400" alt="step2_1_5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/07f2d390-1a3d-c09a-4617-714e380bb444.png">

<br>

プロジェクトのトップ画面で`アンドロイドマーク`を押す。

<img width="400" alt="step2_1_5_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/f3df71a5-7801-55eb-60f3-abd59b0369a4.png">

<br>

Flutterのプロジェクトを作成する。プロジェクト名を例として`my_auth_app`とする。

<img width="400" alt="step2_1_6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/d2d53374-02d7-13b5-6dbf-54bc6b18817a.png">

<br>

`android/app/build.gradle`の`applicationId`をコピーする。

<img width="400" alt="step2_1_7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/b03f0568-dc32-79f7-fd57-14944a2611ad.png">

<br>

アプリを登録するために`applicationId`をパッケージ名にペーストする。他は空欄のままでよく`アプリを登録`を押す。

<img width="400" alt="step2_1_8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/bbd0c616-1adb-9d9b-7fae-532f9b456b10.png">

<br>

構成ファイル`google-services.json`をダウンロードする。

<img width="400" alt="step2_1_9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/863dd837-b78f-5b6f-5257-d354fde9fbf7.png">

<br>

ダウンロードしたファイルを`android/app/build.gradle`と同じフォルダに置く。

<img width="400" alt="step2_1_10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/03796f56-55c6-25a9-bcc0-2ca5179de152.png">

<br>

Firebase SDKを追加するために中段にある`classpath`をコピーする。

<img width="400" alt="step2_1_11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/7c4d8946-dc92-724d-3f29-477ffb1ac8a4.png">

<br>

`classpath`を`android/build.gradle`の中にコピーする。

<img width="400" alt="step2_1_12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/da868332-aba5-603f-0d17-cd808c8b0759.png">

<br>

２つ目のidをコピーする。

<img width="400" alt="step2_1_12_".png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/8fb0656f-0b34-ff5b-7c15-9a436b4b6d95.png">

<br>

`android/app/build.gradle`にidをペーストして`apply plugin: 'com.google.gms.google-services'`の通りに書き換える。

<img width="400" alt="step2_1_13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/4bff7ec5-becd-27b6-5414-c4b91bca8be3.png">

<br>

同様に`dependencies`の中の`implementation`の2行をコピーして`android/app/build.gradle`にペーストする。

<img width="400" alt="step2_1_14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/50e16093-9b1c-ee37-a95f-bce474812aba.png">

<br>

`FirebaseのSDKを追加`にて`次へ`を押してアプリへのFirebaseの追加を完了する。

<img width="400" alt="step2_1_15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/cfa19c08-b631-c1b3-8d7e-936e11c381da.png">

<br>

2つのパッケージを`dart package`で検索、コピーしたバージョン情報を`pubspec.yaml`にペースト、右上の`Pub get`を押す。

<img width="400" alt="step2_1_16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/fee73ef1-065e-71ce-aa62-f95d1e013983.png">

<br>

AuthenticationのUsersタブを開いて下部の様な画面になっていることを確認する。この後、アプリから登録した認証情報が画面に表示される様に様になる。

<img width="400" alt="step2_1_17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/0ee55710-aee3-e006-d892-b3356841dd7a.png">

<br>

`android/app/build.gradle`のminSdkVersionを23に、targetSdkVersionを30に変更する。アプリを作成する時期に応じてこれらの数字は増えていく。最適な数字についてはアプリをビルドするときにヒントとして推奨値をAndroid Studioが教えてくれる。

<img width="400" alt="step2_1_18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/00439dba-e870-74e7-6f7c-8c90ee61821d.png">

<br>

lib/main.dartに認証用のコードを記述。
まず、import文でパッケージを読み込む。

``` .dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
```

FlutterでFirebaseのパッケージを使用する前にFirebase.initializeApp()を呼び出す必要がある。以下のコマンドはFirebaseのバージョンが古いと記述が異なる。
`WidgetsFlutterBinding.ensureInitialized();`
`await Firebase.initializeApp();`
MyAppというStatelessWidgetを呼び出す。

``` .dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(new MyApp());
}
```

StatelessWidgetからAuthAppPageという名前のStatefulWidgetを呼び出す。

``` .dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Firebase_Auth_App',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: const AuthAppPage(title: 'Firebase_Auth_App'),
    );
  }
}
```
以下にコアとなるユーザー登録とログイン確認のコードを記述する。

``` .dart
class AuthAppPage extends StatefulWidget {
  const AuthAppPage({Key? key, required this.title}) : super(key: key);
  final String title;
  @override
  State<AuthAppPage> createState() => _AuthAppPageState();
}

class _AuthAppPageState extends State<AuthAppPage> {
  // Registration Field Email Address
  String registerUserEmail = "";
  // Registration Field Password
  String registerUserPassword = "";
  // Login field email address
  String loginUserEmail = "";
  // Login field password (login)
  String loginUserPassword = "";
  // View information about registration and login
  String DebugText = "";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          padding: EdgeInsets.all(10),
          child: Column(
            children: <Widget>[
              TextFormField(
                // Set labels for text input
                decoration: InputDecoration(labelText: "Mail Address"),
                onChanged: (String value) {
                  setState(() {
                    registerUserEmail = value;
                  });
                },
              ),
              TextFormField(
                decoration: InputDecoration(labelText: "6 character long Password"),
                // Mask not to show password
                obscureText: true,
                onChanged: (String value) {
                  setState(() {
                    registerUserPassword = value;
                  });
                },
              ),
              const SizedBox(height: 1),
              ElevatedButton(
                onPressed: () async {
                  try {
                    // User Registration
                    final FirebaseAuth auth = FirebaseAuth.instance;
                    final UserCredential result =
                    await auth.createUserWithEmailAndPassword(
                      email: registerUserEmail,
                      password: registerUserPassword,
                    );
                    // Registered User Information
                    final User user = result.user!;
                    setState(() {
                      DebugText = "Register OK：${user.email}";
                    });
                  } catch (e) {
                    // Failed User Information
                    setState(() {
                      DebugText = "Register Fail：${e.toString()}";
                    });
                  }
                },
                child: Text("User Registration"),
              ),

              const SizedBox(height: 1),
              TextFormField(
                decoration: InputDecoration(labelText: "Mail Address"),
                onChanged: (String value) {
                  setState(() {
                    loginUserEmail = value;
                  });
                },
              ),
              TextFormField(
                decoration: InputDecoration(labelText: "Password"),
                obscureText: true,
                onChanged: (String value) {
                  setState(() {
                    loginUserPassword = value;
                  });
                },
              ),
              const SizedBox(height: 1),
              ElevatedButton(
                onPressed: () async {
                  try {
                    // Try login
                    final FirebaseAuth auth = FirebaseAuth.instance;
                    final UserCredential result =
                    await auth.signInWithEmailAndPassword(
                      email: loginUserEmail,
                      password: loginUserPassword,
                    );
                    // Succeeded to login
                    final User user = result.user!;
                    setState(() {
                      DebugText = "Succeeded to Login：${user.email}";
                    });
                  } catch (e) {
                    // Failed to login
                    setState(() {
                      DebugText = "Failed to Login：${e.toString()}";
                    });
                  }
                },
                child: Text("Login"),
              ),
              const SizedBox(height: 8),
              Text(DebugText),
            ],
          ),
        ),
      ),
    );
  }
}
```

アプリを実行すると以下の画面が表示される。上２つのテキスト欄がユーザー登録用、下2つのテキスト欄がログイン確認用である。以下にユーザー登録とログインで幾つかのステータスをデバッグ用ステータスバーで確認できるので以下に紹介する。

<img width="200" alt="step2_1_30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/6b6033e0-620a-c0d1-db53-f4f2c969e5b5.png">

<br>

まずユーザー登録用のテキスト欄にメールアドレスを記入。次にパスワードを記入。パスワードが6文字以下だとデバッグ用ステータスバーに6文字以上の記入を求めるエラー文が表示される。

<img width="200" alt="step2_1_31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/cc989c27-9258-905a-ecad-35df93d8278f.png">

<br>

メールアドレスが既に存在している場合はデバッグ用ステータスバーに既に同じユーザーメールアドレスが存在するエラー文が表示される。

<img width="200" alt="step2_1_22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/85febb25-7bc4-0436-9e75-1010029c5df4.png">

<br>

まだ未登録のユーザーメールアドレス、6文字以上のパスワードを記入して`User Registrationボタン`を押す。

<img width="200" alt="step2_1_22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/810edfc5-dc32-7595-cb5b-90bcdc8fe7d4.png">

<br>


Firebaseにユーザータブを開くと、登録したユーザー情報が追加されていることを確認できる
<img width="400" alt="step2_1_22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/e0881f09-18a3-ef4e-e7e6-4ba4d30358de.png">

<br>

正しくないパスワードでログインしようとするデバッグ用ステータスバーにユーザー情報が間違っているというエラー文が表示される。

<img width="200" alt="step2_1_22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/0cfd5434-0e1e-4001-ca54-8e4fe9aba1c8.png">

<br>

正しいユーザー情報でログインするとデバッグ用ステータスバーにSucceededの文字が表示される。

<img width="200" alt="step2_1_22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/892339/40dad88f-271a-b736-86a7-0bad4bb2d11e.png">


# 3.まとめ
Firebase AuthenticationをFlutterで実装する方法を学んだ。