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

1. 始める前に

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

前提条件

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

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

作成するアプリの概要

この Codelab では、Firebase for Authentication を使用して、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
  3. プロジェクト作成オプションをクリックします。プロンプトが表示されたら、Firebase の利用規約に同意します。このアプリではアナリティクスを使用しないため、Google アナリティクスの設定はスキップします。d1fcec48bf251eaa.png

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

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

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

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

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

  1. Firebase コンソールの左側のパネルで [Build] メニューを開きます。
  2. [認証] をクリックし、[使ってみる] ボタン、[ログイン方法] タブの順にクリックします(または、[ログイン方法] タブに直接移動します)。
  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 に必要です。

CLI をインストールする方法はいくつかあります。ご使用のオペレーティング システムで利用可能なすべてのオプションについては、firebase.google.com/docs/cli をご覧ください。

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

  1. Google アカウントで Firebase にログインするには、次のコマンドを実行します。
    firebase login
    
  2. このコマンドにより、ローカルマシンが Firebase に接続し、Firebase プロジェクトへのアクセスが許可されます。
  3. Firebase プロジェクトを一覧表示し、CLI が正しくインストールされていて、アカウントにアクセスできることをテストします。次のコマンドを実行します。
    firebase projects:list
    
  4. 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-codelab1359cdeb83204baa.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 という静的値が表示されます。

lib/firebase_options.dart

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

class DefaultFirebaseOptions {
  static FirebaseOptions get currentPlatform {
    if (kIsWeb) {
      return web;
    }

    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        return android;
      case TargetPlatform.iOS:
        return ios;
      case TargetPlatform.macOS:
        return macos;
      default:
        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.firebasestorage.app',
    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.firebasestorage.app',
  );

  static const FirebaseOptions ios = FirebaseOptions(
    apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
    appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
    messagingSenderId: '963656261848',
    projectId: 'flutterfire-ui-codelab',
    storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
    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.firebasestorage.app',
    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 firebase_auth firebase_ui_auth

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

Firebase を初期化する

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

lib/main.dart

import 'package:firebase_core/firebase_core.dart';                  // Add this import
import 'package:flutter/material.dart';

import 'app.dart';
import 'firebase_options.dart';                                     // And this import

// TODO(codelab user): Get API key
const clientId = 'YOUR_CLIENT_ID';

void main() async {
  // Add from here...
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  // To here.

  runApp(const MyApp(clientId: clientId));
}

このコードは次の 2 つの処理を行います。

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

4. 最初の Firebase UI Auth ページを追加

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

マテリアル アプリまたは Cupertino アプリ

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

lib/app.dart

import 'package:flutter/material.dart';

import 'auth_gate.dart';

class MyApp extends StatelessWidget {
  const MyApp({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: AuthGate(clientId: clientId),
    );
  }
}

認証ステータスを確認する

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

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

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

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

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

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

lib/auth_gate.dart

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

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

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

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

SignInScreen は、FlutterFire UI パッケージのウィジェットです。この Codelab の次のステップでは、この点について詳しく説明します。この時点でアプリを実行すると、空白のログイン画面が表示されます。

5. ログイン画面

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

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

この場合も、必要なコードは数行のみです。AuthGate ウィジェットのコードを思い出してください。

lib/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, required this.clientId});

  final String clientId;

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

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

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

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

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

headerBuilder

SignInScreen.headerBuilder 引数を使用すると、ログイン フォームの上に任意のウィジェットを追加できます。このウィジェットは、モバイル デバイスなどの狭い画面にのみ表示されます。ワイド画面では、SignInScreen.sideBuilder を使用できます。これはこの Codelab の後半で説明します。

lib/auth_gate.dart ファイルを次のように更新します。

lib/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, required this.clientId});

  final String clientId;

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

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

The headerBuilder argument requires a function of the type HeaderBuilder, which
is defined in the FlutterFire UI package.

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

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

73d7548d91bbd2ab.png

字幕ビルダー

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

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

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

lib/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, required this.clientId});

  final String clientId;

  @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'),
                ),
              );
            },
            subtitleBuilder: (context, action) {                     // Add from here...
              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!'),
              );
            },                                                       // To here.
          );
        }

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

footerBuilder 引数は subtitleBuilder と同じです。画像ではなくテキストを対象としているため、BoxConstraintsshrinkOffset は公開されません。もちろん、任意のウィジェットを追加することもできます。

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

lib/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, required this.clientId});

  final String clientId;

  @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'),
                ),
              );
            },
            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) {                       // Add from here...
              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),
                ),
              );
            },                                                       // To here.
          );
        }

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

Side Builder

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

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

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

lib/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, required this.clientId});

  final String clientId;

  @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'),
                ),
              );
            },
            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 ウェブまたは macOS を使用している場合)。

8dc60b4e5d7dd2d0.png

ユーザーを作成する

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

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

  1. Firebase コンソールの [ユーザー] テーブルに移動します。[flutterfire-ui-codelab] を選択します。別の名前を使用した場合は、別のプロジェクトを選択します。次の表が表示されます。f038fd9a58ed60d9.png
  2. [ユーザーを追加] ボタンをクリックします。2d78390d4c5dbbfa.png
  3. 新しいユーザーのメールアドレスとパスワードを入力します。以下の画像に入力したように、メールアドレスとパスワードは偽のものでかまいません。使用できますが、偽のメールアドレスを使用した場合、[パスワードをお忘れの場合] 機能は使用できません。62ba0feb33d54add.png
  4. [ユーザーを追加] 32b236b3ef94d4c7.png をクリックします。

これで、Flutter アプリに戻り、ログインページを使用してユーザーをログインさせることができます。アプリの表示は次のようになります。

dd43d260537f3b1a.png

6. プロフィール画面

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

ProfileScreen ウィジェットを追加

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

lib/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: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

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

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

36487fc4ab4f26a7.png

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

ログアウト

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

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

lib/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: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

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

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

プロフィール ページをカスタマイズする

ログイン画面と同様に、プロフィール ページはカスタマイズできます。まず、現在のページでは、ユーザーがプロフィール ページに移動した後にホームページに戻る方法がありません。ProfileScreen ウィジェットに AppBar を追加して、この問題を修正します。

lib/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: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

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

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

ProfileScreen ウィジェットには、children というオプションの引数もあります。この引数にはウィジェットのリストを指定します。これらのウィジェットは、ProfileScreen の作成に内部で使用されている Column ウィジェットの内部に縦方向に配置されます。ProfileScreen ビルドメソッドのこの Column ウィジェットは、渡された子を [ログアウト] ボタンの上に配置します。

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

lib/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: [
            SizedBox(width: 250, child: Image.asset('assets/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 firebase_ui_oauth_google

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

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

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

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

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

lib/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';  // Add this import
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [
              EmailAuthProvider(),
              GoogleProvider(clientId: clientId),                         // Add this line
            ],
            headerBuilder: (context, constraints, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('assets/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. [ウェブ SDK 構成] 展開パネルをクリックします。
  4. [ウェブ クライアント ID] の値をコピーします。711a79f0d931c60f.png
  5. テキスト エディタに戻り、この ID を clientId という名前のパラメータに渡して、ファイル auth_gate.dartGoogleProvider のインスタンスを更新します。
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. プロジェクトのルートから open ios/Runner.xcworkspace というターミナル コマンドを実行して Xcode を開きます。
  6. Runner ディレクトリを右クリックし、[Add Files to "Runner"] を選択します。858986063a4c5201.png
  7. ファイル マネージャーから GoogleService-Info.plist を選択します。
  8. テキスト エディタ(Xcode 以外)に戻り、ios/Runner/Info.plist ファイルに次の CFBundleURLTypes 属性を追加します。
    <!-- 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 -->
    
  9. ウェブでの設定で追加した GoogleProvider.clientId は、Firebase iOS クライアント ID に関連付けられているクライアント ID に置き換える必要があります。まず、この ID は firebase_options.dart ファイルの iOS 定数の一部として確認できます。iOSClientId に渡された値をコピーします。
    static const FirebaseOptions ios = FirebaseOptions(
      apiKey: 'YOUR API KEY',
      appId: 'YOUR APP ID',
      messagingSenderId: '',
      projectId: 'PROJECT_ID',
      storageBucket: 'PROJECT_ID.firebasestorage.app',
      iosClientId: 'IOS CLIENT ID', // Find your iOS client Id here.
      iosBundleId: 'com.example.BUNDLE',
    );
    
  10. その値を lib/main.dart ファイルの clientId 変数に貼り付けます。

lib/main.dart

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

import 'app.dart';
import 'firebase_options.dart';

const clientId = 'YOUR_CLIENT_ID'; // Replace this value with your Client ID.

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

  runApp(const MyApp(clientId: clientId));
}

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

8. 完了

これで、Flutter 向け Firebase Auth UI の Codelab は終了です。この Codelab の完成版のコードは、GitHub の firebase-auth-flutterfire-ui/complete ディレクトリにあります。

学習した内容

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

次のステップ

詳細

スパーキーがあなたのお祝いに参加します。

2a0ad195769368b1.gif