Firebase Emulator Suite を使用した Flutter アプリのローカル開発

1. 始める前に

この Codelab では、ローカルでの開発時に、Flutter で Firebase Emulator Suite を使用する方法を学びます。Emulator Suite でメール パスワード認証を使用する方法と、Firestore エミュレータでデータを読み書きする方法を学びます。最後に、エミュレータとの間でデータのインポートとエクスポートを行い、開発に戻るたびに同じ架空のデータを操作できるようにします。

前提条件

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

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

作成する内容

この Codelab では、シンプルなジャーナリング アプリケーションの作成について説明します。アプリケーションにはログイン画面があります。また、過去の日記エントリを読んだり、新しい日記エントリを作成したりできる画面があります。

cd5c4753bbee8af.png 8cb4d21f656540bf.png

学習内容

Firebase の使用を開始する方法と、Firebase Emulator Suite を Flutter 開発ワークフローに統合して使用する方法について学習します。Firebase の以下のトピックを取り上げます。

これらのトピックは、Firebase Emulator Suite に対応するために必要な範囲に限ります。この Codelab では、Flutter アプリに Firebase プロジェクトを追加し、Firebase Emulator Suite を使用した開発に重点を置いています。Firebase Authentication や Firestore に関する詳細な説明はありません。これらのトピックに詳しくない場合は、Codelab の Firebase 向け Firebase を理解するをご覧ください。

必要なもの

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

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

最初に、Firebase のウェブ コンソールで Firebase プロジェクトを作成します。この Codelab では主に、ローカルで実行される UI を使用する Emulator Suite を中心に説明しますが、最初に Firebase プロジェクト全体を設定する必要があります。

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

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

fe6aeab3b91965ed.png

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

d1fcec48bf251eaa.png

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

作成するアプリでは、Flutter アプリで使用できる次の 2 つの Firebase プロダクトを使用します。

  • ユーザーがアプリにログインできるようにする Firebase Authentication
  • Cloud Firestore: 構造化データをクラウドに保存し、データが変更されたときにインスタント通知を受け取ります。

これら 2 つのプロダクトは、特別な構成を使用するか、Firebase コンソールを使用して有効にする必要があります。

Cloud Firestore の有効化

Flutter アプリは、Cloud Firestore を使用して履歴項目を保存します。

Cloud Firestore を有効にします。

  1. Firebase コンソールの [構築] セクションで、[Cloud Firestore] をクリックします。
  2. [データベースを作成] をクリックします。99e8429832d23fa3.png
  3. [テストモードで開始] オプションを選択します。セキュリティ ルールに関する免責条項を読みます。テストモードを使用すると、開発中にデータベースに自由に書き込むことができます。[次へ] をクリックします。6be00e26c72ea032.png
  4. データベースの場所を選択します(デフォルトをそのまま使用できます)。このロケーションを後で変更することはできません。278656eefcfb0216.png
  5. [有効にする] をクリックします。

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

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

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

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

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

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

gh repo clone flutter/codelabs flutter-codelabs

サンプルコードのクローンは、Codelab のコレクションが含まれる flutter-codelabs ディレクトリに作成する必要があります。この Codelab のコードは flutter-codelabs/firebase-emulator-suite にあります。

flutter-codelabs/firebase-emulator-suite の下のディレクトリ構造は 2 つの Flutter プロジェクトです。1 つは complete と呼ばれ、スキップしたい場合や、独自のコードを相互参照したい場合に参照できます。もう 1 つのプロジェクトは start という名前です。

最初に使用するコードは、flutter-codelabs/firebase-emulator-suite/start ディレクトリにあります。そのディレクトリを開くか、お好みの IDE にインポートします。

cd flutter-codelabs/firebase-emulator-suite/start

Firebase CLI のインストール

Firebase CLI には、Firebase プロジェクトを管理するためのツールが用意されています。Emulator Suite を使用するには 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 プロジェクトが表示されているはずです。少なくとも firebase-flutter-codelab が表示されます。

FlutterFire CLI をインストールする

FlutterFire CLI は Firebase CLI の上に構築されており、Firebase プロジェクトと Flutter アプリの統合が容易になります。

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

dart pub global activate flutterfire_cli

CLI がインストールされていることを確認します。Flutter プロジェクト ディレクトリ内で次のコマンドを実行し、CLI でヘルプメニューが出力されることを確認します。

flutterfire --help

Firebase CLI と FlutterFire CLI を使用して Firebase プロジェクトを Flutter アプリに追加する

2 つの CLI をインストールすると、いくつかのターミナル コマンドだけで、個々の Firebase プロダクト(Firestore など)の設定、エミュレータのダウンロード、Flutter アプリに Firebase の追加を行えます。

まず、次のコマンドを実行して Firebase の設定を完了します。

firebase init

このコマンドでは、プロジェクトを設定するために必要な一連の質問が表示されます。以下のスクリーンショットは、このフローを示しています。

  1. 機能を選択するように求められたら、[Firestore] と [Emulators] を選択します。(認証オプションはありません。これは、Flutter プロジェクト ファイルから変更可能な構成を使用しないためです)。fe6401d769be8f53.png
  2. 次に、プロンプトが表示されたら、[Use an existing project] を選択します。

f11dcab439e6ac1e.png

  1. 前のステップで作成したプロジェクト(flutter-firebase-codelab)を選択します。

3bdc0c6934991c25.png

  1. 次に、生成されるファイルの命名についていくつか質問します。各質問で Enter キーを押して、デフォルトを選択することをおすすめします。9bfa2d507e199c59.png
  2. 最後に、エミュレータを構成する必要があります。リストから [Firestore] と [Authentication] を選択し、各エミュレータで使用する特定のポートに関する各質問に対して Enter キーを押します。Emulator UI を使用するかどうかを尋ねられたら、デフォルトの [Yes] を選択する必要があります。

プロセスが終了すると、次のスクリーンショットのような出力が表示されます。

重要: 以下のスクリーンショットに示すように、実際の出力は実際の出力とは若干異なる場合があります。エミュレータをすでにダウンロードしている場合、最後の質問はデフォルトで [No] になるからです。

8544e41037637b07.png

FlutterFire を構成する

次に、FlutterFire を使って、Flutter アプリで Firebase を使うために必要な Dart コードを生成します。

flutterfire configure

このコマンドを実行すると、使用する Firebase プロジェクトと設定するプラットフォームを選択するよう求められます。この Codelab の例では Flutter Web を使用していますが、すべてのオプションを使用するように Firebase プロジェクトを設定することもできます。

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

619b7aca6dc15472.png 301c9534f594f472.png

このスクリーンショットは、プロセスの最後に出力されたものです。Firebase を使い慣れている方であれば、コンソールでアプリケーションを作成する必要はなく、FlutterFire CLI によって作成されていることにお気づきでしょう。

12199a85ade30459.png

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

設定の最後のステップとして、適切な Firebase パッケージを Flutter プロジェクトに追加します。ターミナルで、Flutter プロジェクトのルート(flutter-codelabs/firebase-emulator-suite/start)にいることを確認します。次の 3 つのコマンドを実行します。

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add cloud_firestore

このアプリケーションで使用するパッケージはこれのみです。

4. Firebase エミュレータを有効にする

ここまでの作業で、Flutter アプリと Firebase プロジェクトはエミュレータを使えるように設定されていますが、送信 Firebase リクエストをローカルポートに再ルーティングするよう Flutter コードに指示する必要があります。

まず、Firebase 初期化コードとエミュレータのセットアップ コードを main.dart.main 関数に追加します。

main.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import 'app_state.dart';
import 'firebase_options.dart';
import 'logged_in_view.dart';
import 'logged_out_view.dart';


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

 if (kDebugMode) {
   try {
     FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);
     await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
   } catch (e) {
     // ignore: avoid_print
     print(e);
   }
 }

 runApp(MyApp());
}

最初の数行のコードで Firebase が初期化されます。ほぼ例外なく、Flutter アプリで Firebase を扱っている場合は、WidgetsFlutterBinding.ensureInitializedFirebase.initializeApp を呼び出すことから始めるのが一般的です。

その後に if (kDebugMode) 行で始まるコードで、本番環境用の Firebase プロジェクトではなくエミュレータをターゲットにするようアプリに指示します。kDebugMode を使用すると、開発環境にいる場合にのみエミュレータがターゲットになります。kDebugMode は定数値であるため、Dart コンパイラは、リリースモードでそのコードブロックを完全に削除することを認識します。

エミュレータを起動する

Flutter アプリを起動する前に、エミュレータを起動する必要があります。まず、ターミナルで次のコマンドを実行してエミュレータを起動します。

firebase emulators:start

このコマンドを実行するとエミュレータが起動し、操作可能な localhost ポートが公開されます。このコマンドを実行すると、次のような出力が表示されます。

bb7181eb70829606.png

この出力から、実行中のエミュレータと、エミュレータを確認できる場所がわかります。まず、localhost:4000 でエミュレータ UI を確認します。

11563f4c7216de81.png

これは、ローカル エミュレータの UI のホームページです。利用可能なすべてのエミュレータが表示され、各エミュレータにはオンまたはオフのステータスのラベルが付けられています。

5. Firebase Auth エミュレータ

最初に使用するエミュレータは Authentication エミュレータです。UI の Authentication カードで [エミュレータに移動] をクリックして Auth エミュレータを起動すると、次のようなページが表示されます。

3c1bfded40733189.png

このページは、Auth のウェブ コンソール ページと類似しています。オンライン コンソールと同様にユーザーの一覧表があり、ユーザーを手動で追加できます。ここでの大きな違いの一つは、エミュレータで使用できる認証方法のオプションがメールとパスワードのみである点です。ローカルでの開発にはこれで十分です。

次に、ユーザーを Firebase Auth エミュレータに追加し、Flutter UI からそのユーザーにログインするプロセスを説明します。

ユーザーを追加する

[ユーザーを追加] ボタンをクリックし、フォームに次の情報を入力します。

  • 表示名: Dash
  • メール: dash@email.com
  • パスワード: ダッシュワード

フォームを送信すると、テーブルにユーザーが追加されます。これで、そのユーザーでログインするようにコードを更新できます。

logged_out_view.dart

LoggedOutView ウィジェットで更新が必要なコードは、ユーザーがログインボタンを押したときにトリガーされるコールバックのみです。コードを以下のように更新します。

class LoggedOutView extends StatelessWidget {
 final AppState state;
 const LoggedOutView({super.key, required this.state});
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: const Text('Firebase Emulator Suite Codelab'),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
          Text(
           'Please log in',
            style: Theme.of(context).textTheme.displaySmall,
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: ElevatedButton(
             onPressed: () async {
              await state.logIn('dash@email.com', 'dashword').then((_) {
                if (state.user != null) {
                 context.go('/');
                }
              });
              },
              child: const Text('Log In'),
          ),
        ),
      ],
    ),
   ),
  );
 }
}

更新されたコードでは、TODO 文字列が auth Emulator で作成したメールアドレスとパスワードに置き換えられます。次の行の if(true) 行が、state.user が null であるかどうかをチェックするコードに置き換えられています。AppClass のコードは、この点を明らかにしています。

app_state.dart

AppState のコードの 2 つの部分を更新する必要があります。まず、クラスメンバー AppState.user に、Object 型ではなく firebase_auth パッケージの User 型を指定します。

次に、AppState.login メソッドを次のように指定します。

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user; // <-- changed variable type
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 } 
 // ...
}

user の型定義が User? になりました。この User クラスは Firebase Auth で提供され、User.displayName などの必要な情報を提供します(これについては後述します)。

これは、Firebase Auth でメールアドレスとパスワードを使用してユーザーにログインするために必要な基本的なコードです。FirebaseAuth を呼び出してログインし、Future<UserCredential> オブジェクトが返されます。Future の完了時に、UserUserCredential にアタッチされているかどうかを確認します。credential オブジェクトにユーザーがある場合、ユーザーはログインに成功しており、AppState.user プロパティを設定できます。存在しない場合は、エラーが発生し、出力されます。

このメソッドでこのアプリに固有のコード行は(一般的な FirebaseAuth コードではなく)_listenForEntries メソッドの呼び出しだけです。これについては、次のステップで説明します。

TODO: アクション アイコン - アプリを再読み込みし、レンダリングされたらログインボタンを押す。これにより、アプリは上部の「Welcome Back, Person!」というページに移動します。このページへの移動が許可されているため、認証が機能している必要がありますが、ユーザーの実際の名前を表示するために logged_in_view.dart をマイナー アップデートする必要があります。

logged_in_view.dart

LoggedInView.build メソッドの最初の行を変更します。

class LoggedInView extends StatelessWidget {
 final AppState state;
 LoggedInView({super.key, required this.state});

 final PageController _controller = PageController(initialPage: 1);

 @override
 Widget build(BuildContext context) {
   final name = state.user!.displayName ?? 'No Name';

   return Scaffold(
 // ...

次に、この行は AppState オブジェクトの User プロパティから displayName を取得します。この displayName は、最初のユーザーを定義したときにエミュレータで設定されています。ログイン時に、TODO ではなく「Welcome back, Dash!」を表示するようにしました。

6. Firestore エミュレータへのデータの読み取りと書き込み

まず、Firestore エミュレータを確認します。エミュレータ UI のホームページ(localhost:4000)で、Firestore カードの [エミュレータに移動] をクリックします。次のようになります。

エミュレータ:

791fce7dc137910a.png

Firebase コンソール:

e0dde9aea34af050.png

Firestore を使ったことがある方は、このページが Firebase コンソールの Firestore ページと似ていることにお気づきでしょうか。ただし、いくつか大きな違いがあります。

  1. ボタンを 1 回タップするだけですべてのデータを消去できます。これは本番環境データでは危険ですが、迅速なイテレーションに役立ちます。新しいプロジェクトに取り組んでいて、データモデルが変更された場合でも、簡単に移行できます。
  2. [リクエスト] タブがあります。このタブでは、このエミュレータに対する受信リクエストを監視できます。このタブについては、後で詳しく説明します。
  3. [ルール]、[インデックス]、[使用状況] のタブはありません。セキュリティ ルールの作成に役立つツール(次のセクションで説明)がありますが、ローカル エミュレータにセキュリティ ルールを設定することはできません。

以上をまとめると、このバージョンの Firestore には開発に役立つツールがより多く用意されており、本番環境で必要なツールが取り除かれています。

Firestore に書き込む

エミュレータの [Requests] タブについて説明する前に、まずリクエストを行います。そのためにはコードの更新が必要です。まず、アプリにフォームを接続して、新しいジャーナル Entry を Firestore に書き込みます。

Entry を送信するための大まかな流れは次のとおりです。

  1. ユーザーがフォームに入力して Submit ボタンを押しました
  2. UI が AppState.writeEntryToFirebase を呼び出す
  3. AppState.writeEntryToFirebase が Firebase にエントリを追加します。

ステップ 1 と 2 のいずれのコードも変更する必要はありません。ステップ 3 で追加する必要のあるコードは、AppState クラスだけです。AppState.writeEntryToFirebase に次の変更を加えます。

app_state.dart

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user;
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 }

 void writeEntryToFirebase(Entry entry) {
   FirebaseFirestore.instance.collection('Entries').add(<String, String>{
     'title': entry.title,
     'date': entry.date.toString(),
     'text': entry.text,
   });
 }
 // ...
}

writeEntryToFirebase メソッドのコードは、Firestore の「Entries」というコレクションへの参照を取得します。次に、新しいエントリを追加します。エントリは Map<String, String> 型にする必要があります。

このケースでは、Firestore に "Entries" コレクションが存在しないため、Firestore によって Entries コレクションが作成されました。

そのコードを追加したら、アプリをホットリロードまたは再起動し、ログインして EntryForm ビューに移動します。フォームには任意の Strings を入力できます。(この Codelab では単純化されているため、Date フィールドには任意の文字列を指定できます。強力な検証は行われず、DateTime オブジェクトも考慮されません)。

フォームの [送信] をクリックします。アプリでは何も起こりませんが、エミュレータ UI に新しいエントリが表示されます。

Firestore エミュレータの [リクエスト] タブ

UI で Firestore エミュレータに移動し、[データ] タブを確認します。データベースのルートに「Entries」というコレクションが作成されます。これには、フォームに入力した情報が記載された書類が必要です。

a978fb34fb8a83da.png

これで AppState.writeEntryToFirestore が機能したことを確認できました。[Requests] タブでリクエストを詳しく調べることができます。そのタブを今すぐクリックしてください。

Firestore エミュレータ リクエスト

次のようなリストが表示されます。

f0b37f0341639035.png

リストの項目をクリックすると、さまざまな役立つ情報が表示されます。新しい履歴項目を作成するには、リクエストに対応する CREATE リスト項目をクリックします。次のような新しいテーブルが表示されます。

385d62152e99aad4.png

前述のとおり、Firestore エミュレータには、アプリのセキュリティ ルールを開発するためのツールが用意されています。このビューには、このリクエストがセキュリティ ルールのどの行で成功した(または失敗した)かを正確に示します。より堅牢なアプリでは、セキュリティ ルールが拡張され、複数の認証チェックが行われる可能性があります。このビューは、これらの承認ルールの作成とデバッグに役立ちます。

また、メタデータや認証データなど、このリクエストのすべての部分を簡単に検査することもできます。このデータは、複雑な認可ルールを記述するために使用されます。

Firestore から読み取る

Firestore はデータ同期を使用して、更新されたデータを接続済みのデバイスに push します。Flutter コードでは、Firestore のコレクションとドキュメントをリッスン(またはサブスクライブ)でき、データが変更されるたびにコードに通知されます。このアプリでは、AppState._listenForEntries というメソッドで Firestore の更新をリッスンします。

このコードは、それぞれ AppState._entriesStreamController および AppState.entries という StreamController および Stream と組み合わせて使用します。このコードは、UI で Firestore のデータを表示するために必要なすべてのコードと同様に、すでに作成されています。

次のコードと一致するように _listenForEntries メソッドを更新します。

app_state.dart

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user;
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 }

 void writeEntryToFirebase(Entry entry) {
   FirebaseFirestore.instance.collection('Entries').add(<String, String>{
     'title': entry.title,
     'date': entry.date.toString(),
     'text': entry.text,
   });
 }

 void _listenForEntries() {
   FirebaseFirestore.instance
       .collection('Entries')
       .snapshots()
       .listen((event) {
     final entries = event.docs.map((doc) {
       final data = doc.data();
       return Entry(
         date: data['date'] as String,
         text: data['text'] as String,
         title: data['title'] as String,
       );
     }).toList();

     _entriesStreamController.add(entries);
   });
 }
 // ...
}

このコードは、Firestore の「Entries」コレクションをリッスンします。Firestore が新しいデータがあることをこのクライアントに通知すると、そのデータが渡され、_listenForEntries のコードがすべての子ドキュメントをアプリで使用できるオブジェクト(Entry)に変更します。その後、それらのエントリを _entriesStreamController(UI がリッスンしている)という StreamController に追加します。このコードのみ更新が必要です。

最後に、AppState.logIn メソッドは _listenForEntries を呼び出します。これにより、ユーザーがログインした後にリスニング プロセスが開始されます。

// ...
Future<void> logIn(String email, String password) async {
 final credential = await FirebaseAuth.instance
     .signInWithEmailAndPassword(email: email, password: password);
 if (credential.user != null) {
   user = credential.user!;
   _listenForEntries();
 } else {
   print('no user!');
 }
}
// ...

アプリを実行します。コードは次のようになります。

b8a31c7a8900331.gif

7. エミュレータへのデータのエクスポートとインポート

Firebase エミュレータは、データのインポートとエクスポートをサポートしています。インポートとエクスポートを使用すると、開発を中断してから再開する際に、同じデータで開発を続行できます。データファイルを git に commit することもできます。同じデータを同じデータで作業中の他のデベロッパーが使用できます。

エミュレータ データをエクスポートする

まず、既存のエミュレータ データをエクスポートします。エミュレータの実行中に、新しいターミナル ウィンドウを開き、次のコマンドを入力します。

firebase emulators:export ./emulators_data

.emulators_data は引数で、データのエクスポート先を Firebase に指示します。ディレクトリが存在しない場合は作成されます。ディレクトリには任意の名前を付けることができます。

このコマンドを実行すると、コマンドを実行したターミナルに次の出力が表示されます。

i  Found running emulator hub for project flutter-firebase-codelab-d6b79 at http://localhost:4400
i  Creating export directory /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data
i  Exporting data to: /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data
✔  Export complete

エミュレータを実行しているターミナル ウィンドウに切り替えると、次の出力が表示されます。

i  emulators: Received export request. Exporting data to /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data.
✔  emulators: Export complete.

最後に、プロジェクト ディレクトリを確認すると、./emulators_data というディレクトリがあるはずです。このディレクトリには、保存したデータを含む JSON ファイルやその他のメタデータ ファイルが含まれています。

エミュレータ データのインポート

開発ワークフローの一環としてそのデータをインポートし、中断したところから開始できるようになりました。

まず、エミュレータを実行している場合は、ターミナルで CTRL+C を押して停止します。

次に、先ほど確認した emulators:start コマンドを実行します。その際、インポートするデータを指定するフラグを指定します。

firebase emulators:start --import ./emulators_data

エミュレータが起動したら、localhost:4000 のエミュレータ UI に移動すると、前に扱ったものと同じデータが表示されます。

エミュレータを終了するときにデータを自動的にエクスポートする

また、開発セッションの最後に必ずデータをエクスポートするのではなく、エミュレータを終了したときにデータを自動的にエクスポートすることもできます。

エミュレータを起動するときは、2 つの追加フラグを指定して emulators:start コマンドを実行します。

firebase emulators:start --import ./emulators_data --export-on-exit

必要な操作は以上です。これでデータが保存され、このプロジェクトのエミュレータを使用するたびにデータが再読み込みされます。–export-on-exit flag の引数として別のディレクトリを指定することもできますが、デフォルトは –import に渡されたディレクトリです。

これらのオプションは自由に組み合わせて使用できます。以下、ドキュメントの注記です。エクスポート ディレクトリは、フラグ firebase emulators:start --export-on-exit=./saved-data で指定できます。--import が使用されている場合、エクスポート パスはデフォルトで同じになります(例: firebase emulators:start --import=./data-path --export-on-exit)。最後に、必要に応じて --import フラグと --export-on-exit フラグに異なるディレクトリ パスを渡します。

8. お疲れさまでした

これで「Firebase エミュレータと Flutter の使用を開始する」は完了です。この Codelab の最終的なコードは、github の「complete」ディレクトリ(Flutter Codelabs)にあります。

学習した内容

  • Firebase を使用するように Flutter アプリをセットアップする
  • Firebase プロジェクトをセットアップする
  • FlutterFire CLI
  • Firebase CLI
  • Firebase Authentication エミュレータ
  • Firebase Firestore エミュレータ
  • エミュレータ データのインポートとエクスポート

次のステップ

詳細

スパーキーはあなたを誇りに思っています!

2a0ad195769368b1.gif