FirebaseUI を使用して Flutter アプリにユーザー認証フローを追加する

1. 始める前に

この Codelab では、FlutterFire UI パッケージを使用して、Flutter アプリに Firebase Authentication を追加する方法を学びます。このパッケージを使用して、メール/パスワード認証と Google ログイン認証の両方を Flutter アプリに追加します。また、Firebase プロジェクトを設定する方法と、FlutterFire CLI を使用して Flutter アプリで Firebase を初期化する方法も学びます。

前提条件

この Codelab は、Flutter の使用経験があることを前提としています。まだの場合は、まず基本的な使い方を学習してください。次のリンクが役に立ちます。

Firebase の使用経験も必要ですが、Flutter プロジェクトに Firebase を追加したことがない方でも問題ありません。Firebase コンソールや Firebase についてよく知らない場合は、まず次のリンクをご覧ください。

作成する内容

この Codelab では、Authentication に Firebase を使用して、Flutter アプリの認証フローを構築する方法を説明します。アプリケーションには、ログイン画面、「登録」画面、パスワードの再設定画面、ユーザー プロファイル画面があります。

6604fc9157f2c6ae.png eab9509a41074930.png da49189a5838e0bb.png b2ccfb3632b77878.png

学習内容

この Codelab の内容:

  • Flutter アプリに Firebase を追加する
  • Firebase コンソールの設定
  • Firebase CLI を使用して Firebase をアプリケーションに追加する
  • FlutterFire CLI を使用して Dart で Firebase 構成を生成する
  • Flutter アプリに Firebase Authentication を追加する
  • コンソールでの Firebase Authentication の設定
  • firebase_ui_auth パッケージでメールアドレスとパスワードを追加してログインする
  • firebase_ui_auth パッケージを使用してユーザー登録を追加する
  • 「パスワードをお忘れですか?」ページの追加
  • firebase_ui_auth で Google ログインを追加しています
  • 複数のログイン プロバイダと連携するようにアプリを構成する。
  • firebase_ui_auth パッケージを使用してユーザー プロファイル画面をアプリに追加する

この Codelab では、firebase_ui_auth パッケージを使用して堅牢な認証システムを追加することに特に重点を置いています。ご覧のとおり、上記の機能をすべて備えたこのアプリ全体を、約 100 行のコードで実装できます。

必要なもの

  • Flutter と SDK のインストールに関する実用的な知識がある
  • テキスト エディタ(JetBrains IDE、Android Studio、VS Code は Flutter でサポートされています)
  • Google Chrome ブラウザ、またはその他の Flutter の開発ターゲット。(この Codelab の一部のターミナル コマンドでは、Chrome でアプリを実行していることを前提としています)。

2. Firebase プロジェクトを作成して設定する

最初に、Firebase のウェブ コンソールで Firebase プロジェクトを作成します。

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

  1. Firebase にログインします。
  2. Firebase コンソールで [プロジェクトを追加](または [プロジェクトを作成])をクリックし、Firebase プロジェクトの名前を入力します(例: 「FlutterFire-UI-Codelab」)。

df42a5e3d9584b48.png

  1. プロジェクト作成オプションをクリックします。プロンプトが表示されたら、Firebase の利用規約に同意します。このアプリではアナリティクスを使用しないため、Google アナリティクスの設定をスキップします。

d1fcec48bf251eaa.png

Firebase プロジェクトについて詳しくは、Firebase プロジェクトについて理解するをご覧ください。

作成するアプリでは、Firebase Authentication を使用してユーザーがアプリにログインできるようにします。また、新規ユーザーが Flutter アプリケーションから登録できるようにもなります。

Firebase Authentication は、Firebase コンソールを使用して有効にする必要があります。また、有効にした後は特別な構成が必要になります。

Firebase Authentication のメールログインを有効にする

ユーザーがウェブアプリにログインできるようにするには、まずメール/パスワードによるログイン方法を使用します。後で Google ログイン方法を追加します。

  1. Firebase コンソールで、左側のパネルにある [ビルド] メニューを開きます。
  2. [Authentication] をクリックし、[Get Started] ボタン、[Sign-in method] タブの順にクリックします(またはここをクリックして [Sign-in method] タブに直接移動します)。
  3. [ログイン プロバイダ] リストで [メール/パスワード] をクリックし、[有効にする] スイッチをオンにして [保存] をクリックします。58e3e3e23c2f16a4.png

3. Flutter アプリをセットアップする

開始する前に、スターター コードをダウンロードして Firebase CLI をインストールする必要があります。

スターター コードを取得する

コマンドラインから GitHub リポジトリのクローンを作成します。

git clone https://github.com/flutter/codelabs.git flutter-codelabs

または、GitHub の CLI ツールがインストールされている場合は、次のようにします。

gh repo clone flutter/codelabs flutter-codelabs

サンプルコードのクローンは、マシンの flutter-codelabs ディレクトリに作成する必要があります。このディレクトリには、複数の Codelab のコードが格納されています。この Codelab のコードは、サブディレクトリ flutter-codelabs/firebase-auth-flutterfire-ui にあります。

flutter-codelabs/firebase-auth-flutterfire-ui ディレクトリには 2 つの Flutter プロジェクトが含まれています。1 つは complete、もう 1 つは start と呼ばれます。start ディレクトリには未完成のプロジェクトが含まれており、最もよく時間を費やすことになります。

cd flutter-codelabs/firebase-auth-flutterfire-ui/start

先に進まない場合、または完了時にどのような状態になるかを確認する場合は、complete という名前のディレクトリを調べて、相互参照します。

Codelab に沿って自分でコードを追加する場合は、まず flutter-codelabs/firebase-auth-flutterfire-ui/start にある Flutter アプリから始め、Codelab 全体を通してプロジェクトにコードを追加する必要があります。そのディレクトリを開くか、お好みの IDE にインポートします。

Firebase CLI のインストール

Firebase CLI には、Firebase プロジェクトを管理するためのツールが用意されています。この CLI は FlutterFire CLI に必要です。FlutterFire CLI は、後ほどインストールします。

CLI はさまざまな方法でインストールできます。MacOS または Linux を使用している場合は、ターミナルから次のコマンドを実行するのが最も簡単な方法です。

curl -sL https://firebase.tools | bash

CLI をインストールしたら、Firebase で認証する必要があります。

  1. Google アカウントで Firebase にログインするには、次のコマンドを実行します。
firebase login
  1. このコマンドを実行すると、ローカルマシンが Firebase に接続され、Firebase プロジェクトへのアクセス権が付与されます。
  1. Firebase プロジェクトを一覧表示して、CLI が正しくインストールされていて、アカウントにアクセスできることをテストします。次のコマンドを実行します。
firebase projects:list
  1. Firebase コンソールと同じ Firebase プロジェクトが表示されているはずです。少なくとも flutterfire-ui-codelab. が表示されます。

FlutterFire CLI をインストールする

FlutterFire CLI は、Flutter アプリでサポートされているすべてのプラットフォームで Firebase のインストール プロセスを容易にするツールです。Firebase CLI 上に構築されています。

まず、CLI をインストールします。

dart pub global activate flutterfire_cli

CLI がインストールされていることを確認します。次のコマンドを実行して、CLI でヘルプメニューが出力されることを確認します。

flutterfire -—help

Firebase プロジェクトを Flutter アプリに追加する

FlutterFire を構成する

FlutterFire を使用すると、Flutter アプリで Firebase を使用するために必要な Dart コードを生成できます。

flutterfire configure

このコマンドを実行すると、使用する Firebase プロジェクトと設定するプラットフォームを選択するよう求められます。

次のスクリーンショットは、回答する必要があるプロンプトを示しています。

  1. 使用するプロジェクトを選択します。この場合、flutterfire-ui-codelab 1359cdeb83204baa.png を使用します。
  2. 使用するプラットフォームを選択します。この Codelab では、ウェブ、iOS、Android の Flutter に対して Firebase Authentication を構成する手順を示していますが、すべてのオプションを使用するように Firebase プロジェクトを設定することもできます。301c9534f594f472.png
  3. このスクリーンショットは、プロセスの最後に出力されたものです。Firebase を使い慣れている方なら、コンソールでプラットフォーム アプリケーション(Android アプリケーションなど)を作成する必要はなく、FlutterFire CLI で自動的に作成されていることにお気づきでしょう。12199a85ade30459.png

完了したら、テキスト エディタで Flutter アプリを確認します。FlutterFire CLI により、firebase_options.dart という新しいファイルが生成されました。このファイルには FirebaseOptions というクラスが含まれています。このクラスには、各プラットフォームに必要な Firebase 構成を保持する静的変数が含まれています。flutterfire configure の実行時にすべてのプラットフォームを選択した場合は、webandroidiosmacos という名前の静的な値が表示されます。

firebase_options.dart

import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
   show defaultTargetPlatform, kIsWeb, TargetPlatform;

/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
///   options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
 static FirebaseOptions get currentPlatform {
   if (kIsWeb) {
     return web;
   }
   // ignore: missing_enum_constant_in_switch
   switch (defaultTargetPlatform) {
     case TargetPlatform.android:
       return android;
     case TargetPlatform.iOS:
       return ios;
     case TargetPlatform.macOS:
       return macos;
   }

   throw UnsupportedError(
     'DefaultFirebaseOptions are not supported for this platform.',
   );
 }

 static const FirebaseOptions web = FirebaseOptions(
   apiKey: 'AIzaSyCqFjCV_9CZmYeIvcK9FVy4drmKUlSaIWY',
   appId: '1:963656261848:web:7219f7fca5fc70afb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   authDomain: 'flutterfire-ui-codelab.firebaseapp.com',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
   measurementId: 'G-DGF0CP099H',
 );

 static const FirebaseOptions android = FirebaseOptions(
   apiKey: 'AIzaSyDconZaCQpkxIJ5KQBF-3tEU0rxYsLkIe8',
   appId: '1:963656261848:android:c939ccc86ab2dcdbb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
 );

 static const FirebaseOptions ios = FirebaseOptions(
   apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
   appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
   iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
   iosBundleId: 'com.example.complete',
 );

 static const FirebaseOptions macos = FirebaseOptions(
   apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
   appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
   iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
   iosBundleId: 'com.example.complete',
 );
}

Firebase では、「アプリケーション」という言葉を、Firebase プロジェクトに含まれる特定のプラットフォーム用の特定のビルドを指します。たとえば、FlutterFire-ui-codelab という Firebase プロジェクトには、Android 用、iOS 用、MacOS 用、ウェブ用の複数のアプリケーションがあります。

メソッド DefaultFirebaseOptions.currentPlatform は、Flutter で公開されている TargetPlatform 列挙型を使って、アプリが実行されているプラットフォームを検出し、正しい Firebase アプリケーションに必要な Firebase 構成値を返します。

Firebase パッケージを Flutter アプリに追加する

設定の最後のステップとして、適切な Firebase パッケージを Flutter プロジェクトに追加します。firebase_options.dart ファイルは、まだ追加されていない Firebase パッケージに依存しているため、エラーがあるはずです。ターミナルで、Flutter プロジェクトのルート(flutter-codelabs/firebase-emulator-suite/start)にいることを確認します。次の 3 つのコマンドを実行します。

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add firebase_ui_auth

この時点で必要なパッケージはこれだけです。

Firebase を初期化する

追加されたパッケージと DefaultFirebaseOptions.currentPlatform, を使用するために、main.dart ファイル内の main 関数のコードを更新します。

main.dart

void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(
   options: DefaultFirebaseOptions.currentPlatform,
 );


 runApp(const MyApp());
}

このコードは 2 つのことを行います。

  1. WidgetsFlutterBinding.ensureInitialized() は、Flutter フレームワークが完全に起動するまでアプリ ウィジェット コードを実行しないよう Flutter に指示します。Firebase はネイティブ プラットフォーム チャネルを使用するため、フレームワークを実行する必要があります。
  2. Firebase.initializeApp は、Flutter アプリと Firebase プロジェクト間の接続を設定します。DefaultFirebaseOptions.currentPlatform は、生成された firebase_options.dart ファイルからインポートされます。この静的な値により、実行されているプラットフォームが検出され、対応する Firebase キーが渡されます。

4. 最初の Firebase UI 認証ページを追加する

Auth 用の Firebase UI には、アプリケーションの画面全体を表すウィジェットが用意されています。これらの画面では、ログイン、登録、パスワードをお忘れの場合、ユーザー プロフィールなど、アプリケーション全体でさまざまな認証フローを処理します。まず、メイン アプリケーションに対する認証ガードとして機能するランディング ページをアプリに追加します。

Material または Cupertino アプリ

FlutterFire UI では、アプリケーションが MaterialApp または CupertinoApp のいずれかでラップされている必要があります。選択した内容に応じて、UI には Material ウィジェットと Cupertino ウィジェットの違いが自動的に反映されます。この Codelab では、app.dart のアプリにすでに追加されている MaterialApp を使用します。

app.dart

import 'package:flutter/material.dart';
import 'auth_gate.dart';

class MyApp extends StatelessWidget {
 const MyApp({super.key});
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: const AuthGate(),
   );
 }
}

認証状態を確認する

ログイン画面を表示する前に、ユーザーが現在認証されているかどうかを判断する必要があります。これを確認する最も一般的な方法は、Firebase Auth プラグインを使用して FirebaseAuth の authStateChanges をリッスンすることです。

上記のコードサンプルでは、MaterialApp がその build メソッドで AuthGate ウィジェットを作成しています。(これはカスタム ウィジェットであり、FlutterFire UI では提供されません)。

このウィジェットを更新して、authStateChanges ストリームを含める必要があります。

authStateChanges API は、現在のユーザー(ログインしている場合)を含む Stream を返し、ログインしていない場合は null を返します。アプリケーションでこの状態をサブスクライブするには、Flutter の StreamBuilder ウィジェットを使用してストリームを渡します。

StreamBuilder は、渡されたストリームからのデータの最新スナップショットに基づいて自身をビルドするウィジェットです。ストリームが新しいスナップショットを出力すると、自動的に再ビルドされます。

auth_gate.dart のコードを更新します。

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [],
          );
        }

        return const HomeScreen();
      },
    );
  }
}
  • StreamBuilder.stream には、前述のストリームである FirebaseAuth.instance.authStateChanged が渡されます。ユーザーが認証されると、このストリームは Firebase User オブジェクトを返します。(そうでない場合は null を返します)。
  • 次に、snapshot.hasData を使用して、ストリームの値に User オブジェクトが含まれているかどうかを確認します。
  • 存在しない場合は、SignInScreen ウィジェットを返します。現在、この画面では何も起こりません。これは次のステップで更新します。
  • それ以外の場合は、HomeScreen を返します。これは、認証されたユーザーのみがアクセスできるアプリケーションの主要部分です。

SignInScreen は、FlutterFire UI パッケージのウィジェットです。これは、この Codelab の次のステップの焦点となります。この時点でアプリを実行すると、空白のログイン画面が表示されます。

5. ログイン画面

FlutterFire UI の SignInScreen ウィジェットには、次の機能が追加されています。

  • ユーザーがログインできるようにする
  • ユーザーがパスワードを忘れた場合は、[パスワードをお忘れの方] をタップすると、パスワードを再設定するためのフォームが表示されます。
  • ユーザーがまだ登録していない場合は、[登録] をタップすると、登録できる別のフォームが表示されます。

これも、わずか数行のコードです。AuthGate ウィジェットのコードをもう一度確認してみましょう。

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [
              EmailAuthProvider(), // new
            ],
          );
        }

        return const HomeScreen();
      },
    );
  }
}

SignInScreen ウィジェットとその providers 引数は、前述のすべての機能を取得するために必要な唯一のコードです。ログイン画面が表示され、[メールアドレス] と [パスワード] のテキスト入力と、[ログイン] ボタンが表示されます。

機能的だが、スタイルには欠けている。このウィジェットは、ログイン画面の外観をカスタマイズするためのパラメータを公開しています。たとえば、会社のロゴを追加できます。

ログイン画面をカスタマイズする

headerBuilder

SignInScreen.headerBuilder 引数を使用して、ログイン フォームの上に任意のウィジェットを追加できます。auth_gate.dart ファイルを次のコードで更新します。

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('assets/flutterfire_300x.png'),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

headerBuilder 引数には HeaderBuilder 型の関数が必要です。これは FlutterFire UI パッケージで定義されています。

typedef HeaderBuilder = Widget Function(
 BuildContext context,
 BoxConstraints constraints,
 double shrinkOffset,
);

これはコールバックであるため、BuildContextBoxConstraints などの使用可能な値を公開し、ウィジェットを返す必要があります。どちらのウィジェットが画面の上部に表示されますか。この例では、新しいコードによって画面の上部に画像が追加されます。アプリケーションは次のようになります。

73d7548d91bbd2ab.png

字幕作成ツール

ログイン画面には、画面をカスタマイズできる 3 つの追加パラメータ(subtitleBuilderfooterBuildersideBuilder)が用意されています。

subtitleBuilder は、コールバック引数に AuthAction 型のアクションが含まれている点で若干異なります。AuthAction は、ユーザーが表示している画面が「ログイン」画面か「登録」画面かを検出するために使用できる列挙型です。

subtitleBuilder を使用するように auth_gate.dart のコードを更新します。

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
              EmailAuthProvider()
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

アプリケーションを再読み込みすると、次のようになります。

footerBuilder 引数は subtitleBuilder と同じものです。BoxConstraintsshrinkOffset は画像ではなくテキストを対象としているため、公開しません。(ウィジェットは自由に追加できます)。

このコードを使用して、ログイン画面にフッターを追加します。

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider()
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }}

サイドビルダー

SignInScreen.sidebuilder 引数はコールバックを受け入れます。今回は、そのコールバックの引数は BuildContextdouble shrinkOffset です。sideBuilder が返すウィジェットは、ワイド スクリーンでのみ、ログイン フォームの左側に表示されます。つまり、ウィジェットはデスクトップ アプリとウェブアプリにのみ表示されます。

内部的には、FlutterFire UI はブレークポイントを使用して、ヘッダー コンテンツを(モバイルなどの縦長の画面)表示するか、サイド コンテンツ(ワイド画面、デスクトップ、ウェブ)を表示するかを決定します。具体的には、画面の幅が 800 ピクセルを超える場合、サイドビルダーのコンテンツは表示されますが、ヘッダーのコンテンツは表示されません。画面の幅が 800 ピクセル未満の場合は、その逆になります。

auth_gate.dart のコードを更新して、sideBuilder ウィジェットを追加します。

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
           sideBuilder: (context, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
         );
       }
       return const HomeScreen();
     },
   );
 }
}

ウィンドウの幅を広げると、アプリは次のようになります(Flutter Web または MacOS を使用している場合)。

8dc60b4e5d7dd2d0.png

ユーザーを作成する

これで、この画面のコードはすべて完成しました。ただし、ログインする前にユーザーを作成する必要があります。これを行うには、[登録] 画面を使用するか、Firebase コンソールでユーザーを作成します。

コンソールを使用するには:

  1. Firebase コンソールで [ユーザー] の表に移動します。
  2. こちらをクリック
  3. 「flutterfire-ui-codelab」(または別の名前を使用した場合は別のプロジェクト)を選択します。次のテーブルが表示されます。

f038fd9a58ed60d9.png

  1. [ユーザーを追加] ボタンをクリックします。

2d78390d4c5dbbfa.png

  1. 新しいユーザーのメールアドレスとパスワードを入力します。下の画像に入力したように、これは偽のメールとパスワードである可能性があります。それ自体は機能しますが、「パスワードをお忘れの場合」機能は、偽のメール アドレスを使用すると機能しません。

62ba0feb33d54add.png

  1. [ユーザーを追加] をクリックします

32b236b3ef94d4c7.png

これで、Flutter アプリケーションに戻り、ログインページからユーザーをログインできるようになります。アプリは次のようになります。

dd43d260537f3b1a.png

6. プロフィール画面

FlutterFire UI は ProfileScreen ウィジェットも提供しており、これも数行のコードで多くの機能を提供します。

ProfileScreen ウィジェットを追加する

テキスト エディタで home.dart ファイルに移動します。これを次のコードで更新します。

home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => const ProfileScreen(),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

注目すべき新しいコードは、IconButton.isPressed method. に渡されるコールバックです。この IconButton が押されると、アプリケーションは新しい匿名ルートを作成して、そのルートに移動します。このルートにより、MaterialPageRoute.builder コールバックから返された ProfileScreen ウィジェットが表示されます。

アプリを再読み込みし、右上(アプリバー)のアイコンを押すと、次のようなページが表示されます。

36487fc4ab4f26a7.png

これは、FlutterFire UI ページに用意されている標準 UI です。ボタンとテキスト フィールドはすべて Firebase Auth と連携しており、すぐに使用できます。たとえば、[Name] テキスト フィールドに名前を入力すると、FlutterFire UI は FirebaseAuth.instance.currentUser?.updateDisplayName メソッドを呼び出し、その名前を Firebase に保存します。

ログアウト

現時点では、[ログアウト] ボタンを押しても、アプリは変更されません。ログアウトしますが、AuthGate ウィジェットには戻りません。これを実装するには、ProfileScreen.actions パラメータを使用します。

まず home.dart のコードを更新します。

home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      })
                    ],
                  ),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

ここで、ProfileScreen のインスタンスを作成するときは、ProfileScreen.actions 引数にアクションのリストも渡します。これらのアクションのタイプは FlutterFireUiAction です。FlutterFireUiAction のサブタイプにはさまざまなクラスがあり、通常は、さまざまな認証状態の変化に対応するようにアプリに伝えるために使用します。SignedOutAction は、Firebase 認証状態が currentUser が null に変わったときに提供するコールバック関数を呼び出します。

SignedOutAction がトリガーされたときに Navigator.of(context).pop() を呼び出すコールバックを追加することで、アプリは前のページに移動します。このサンプルアプリには永続的なルートが 1 つしかなく、ユーザーがログインしていない場合はログインページが表示され、ユーザーがいる場合はホームページが表示されます。これはユーザーがログアウトしたときに発生するため、アプリにログインページが表示されます。

プロフィール ページのカスタマイズ

ログインページと同様に、プロフィール ページはカスタマイズできます。まず、現在のページには、ユーザーがプロフィール ページを表示した後、ホームページに戻る機能がありません。この問題を修正するには、ProfileScreen ウィジェットに AppBar を指定します。

home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
 const HomeScreen({super.key});

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       actions: [
         IconButton(
           icon: const Icon(Icons.person),
           onPressed: () {
             Navigator.push(
               context,
               MaterialPageRoute<ProfileScreen>(
                 builder: (context) => ProfileScreen(
                   appBar: AppBar(
                     title: const Text('User Profile'),
                   ),
                   actions: [
                     SignedOutAction((context) {
                       Navigator.of(context).pop();
                     })
                   ],
                 ),
               ),
             );
           },
         )
       ],
       automaticallyImplyLeading: false,
     ),
     body: Center(
       child: Column(
         children: [
           Image.asset('dash.png'),
           Text(
             'Welcome!',
             style: Theme.of(context).textTheme.displaySmall,
           ),
           const SignOutButton(),
         ],
       ),
     ),
   );
 }
}

ProfileScreen.appBar 引数は、Flutter マテリアル パッケージの AppBar ウィジェットを受け入れるため、自分で作成して Scaffold に渡した他の AppBar と同様に扱うことができます。この例では、[戻る] ボタンを自動的に追加するデフォルトの機能が維持され、画面にタイトルが付けられています。

プロフィール画面に子を追加する

ProfileScreen ウィジェットには、child というオプションの引数もあります。この引数はウィジェットのリストを受け取ります。これらのウィジェットは、ProfileScreen を作成するためにすでに内部的に使用されている列ウィジェット内に垂直に配置されます。ProfileScreen ビルドメソッドのこの Column ウィジェットは、渡された子を [Sign out] ボタンの上に配置します。

home.dart のコードを更新し、ログイン画面と同様にここに会社のロゴが表示されるようにします。

home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    appBar: AppBar(
                      title: const Text('User Profile'),
                    ),
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      })
                    ],
                    children: [
                      const Divider(),
                      Padding(
                        padding: const EdgeInsets.all(2),
                        child: AspectRatio(
                          aspectRatio: 1,
                          child: Image.asset('flutterfire_300x.png'),
                        ),
                      ),
                    ],
                  ),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

アプリを再読み込みすると、次のように表示されます。

ebe5792b765dbf87.png

7. マルチプラットフォームの Google 認証ログイン

FlutterFire UI は、Google、Twitter、Facebook、Apple、GitHub などのサードパーティ プロバイダで認証するためのウィジェットと機能も備えています。

Google 認証と統合するには、ネイティブの認証フローを処理する公式の firebase_ui_oauth_google プラグインとその依存関係をインストールします。ターミナルで flutter プロジェクトのルートに移動し、次のコマンドを入力します。

flutter pub add google_sign_in
flutter pub add firebase_ui_oauth_google

Google ログイン プロバイダを有効にする

次に、Firebase コンソールで Google プロバイダを有効にします。

  1. コンソールの [Authentication ログイン プロバイダ] 画面に移動します。
  2. [新しいプロバイダを追加] をクリックします。8286fb28be94bf30.png
  3. [Google] を選択します。c4e28e6f4974be7f.png
  4. [有効にする] スイッチを切り替えて、[保存] をクリックします。e74ff86990763826.png
  5. 設定ファイルのダウンロードに関する情報のモーダルが表示されたら、[完了] をクリックします。
  6. Google ログイン プロバイダが追加されていることを確認します。5329ce0543c90d95.png

Google ログインボタンを追加する

Google ログインを有効にして、定型化された Google ログインボタンを表示するために必要なウィジェットをログインページに追加します。auth_gate.dart ファイルに移動し、コードを次のように更新します。

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:firebase_ui_oauth_google/firebase_ui_oauth_google.dart'; // new
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
             GoogleProvider(clientId: "YOUR_WEBCLIENT_ID"),  // new
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
           sideBuilder: (context, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

ここでの新しいコードは、SignInScreen ウィジェットの設定に GoogleProvider(clientId: "YOUR_WEBCLIENT_ID") を追加しただけです。

これを追加してアプリを再読み込みすると、Google ログインボタンが表示されます。

aca71a46a011bfb5.png

ログインボタンを構成する

このボタンは、追加の設定がないと機能しません。Flutter Web で開発を行っている場合は、これが機能するために追加する必要のあるステップだけです。他のプラットフォームでは追加の手順が必要です。これについては後述します。

  1. Firebase コンソールの [認証プロバイダ] ページに移動します。
  2. Google プロバイダをクリックします。9b3a325c5eca6e49.png
  3. [Web SDK configuration] 展開パネルをクリックします。
  4. [ウェブ クライアント ID] 711a79f0d931c60f.png から値をコピーします
  5. テキスト エディタに戻り、この ID を clientId 名前付きパラメータに渡して、auth_gate.dart ファイル内の GoogleProvider のインスタンスを更新します。
GoogleProvider(
   clientId: "YOUR_WEBCLIENT_ID"
)

ウェブ クライアント ID を入力したら、アプリを再読み込みします。[Google でログイン] ボタンを押すと、新しいウィンドウ(ウェブを使用している場合)が開き、Google ログインフローに進むことができます。最初は次のような内容です。

14e73e3c9de704bb.png

iOS の構成

これを iOS で機能させるには、追加の設定プロセスがあります。

  1. Firebase コンソールの [プロジェクトの設定] 画面に移動します。Firebase アプリが fefa674acbf213cc.png のように一覧表示されたカードが表示されます。
  2. [iOS] をクリックします。アプリの名前は私のものとは異なります。flutter-codelabs/firebase-auth-flutterfire-ui/start プロジェクトを使用してこの Codelab を進めた場合、私の場合は「complete」は「start」になります。
  3. [GoogleServices-Info.plist] というボタンをクリックして、必要な構成ファイルをダウンロードします。f89b3192871dfbe3.png
  4. ダウンロードしたファイルを ディレクトリにドラッグ&ドロップします。Flutter プロジェクトの /ios/Runner
  5. プロジェクトのルートから次のターミナル コマンドを実行して Xcode を開きます。

ios/Runner.xcworkspace を開く

  1. Runner ディレクトリを右クリックして、[Add Files to "Runner"] を選択します。858986063a4c5201.png
  2. ファイル マネージャーから GoogleService-Info.plist を選択します。
  3. Xcode 以外のテキスト エディタに戻り、以下の CFBundleURLTypes 属性を [my_project]/ios/Runner/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 -->

Flutter アプリがすでに iOS で実行されている場合は、アプリを完全にシャットダウンしてから再度実行する必要があります。それ以外の場合は、iOS でアプリを実行します。

8. お疲れさまでした

これで、Flutter 用の Firebase Auth UI の Codelab は終了です。この Codelab の最終的なコードは、github の「complete」ディレクトリ(Flutter Codelabs)にあります。

学習した内容

  • Firebase を使用するように Flutter アプリをセットアップする
  • Firebase コンソールで Firebase プロジェクトを設定する
  • FlutterFire CLI
  • Firebase CLI
  • Firebase Authentication の使用
  • FlutterFire UI を使用して、Flutter アプリで Firebase Auth を簡単に処理する

次のステップ

詳細

Sparky が皆さんとお祝いします!

2a0ad195769368b1.gif