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 についての詳しい説明は行いません。これらのトピックに慣れていない場合は、まず Flutter の Firebase について知る Codelab をご覧になることをおすすめします。

必要なもの

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

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

最初に完了する必要があるタスクは、Firebase のウェブ コンソールで Firebase プロジェクトを作成することです。この Codelab の大部分は、ローカルで実行される UI を使用する Emulator Suite に焦点を当てていますが、まず完全な Firebase プロジェクトを設定する必要があります。

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

  1. Google アカウントを使用して Firebase コンソールにログインします。
  2. ボタンをクリックして新しいプロジェクトを作成し、プロジェクト名(例: Firebase-Flutter-Codelab)を入力します。
  3. [続行] をクリックします。
  4. Firebase の利用規約が表示されたら、内容を読み、同意して [続行] をクリックします。
  5. (省略可)Firebase コンソールで AI アシスタンス(「Gemini in Firebase」)を有効にします。
  6. この Codelab では Google アナリティクスは必要ないため、Google アナリティクスのオプションをオフに切り替えます
  7. [プロジェクトを作成] をクリックし、プロジェクトのプロビジョニングが完了するまで待ってから、[続行] をクリックします。

Firebase プロジェクトの詳細については、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 など)の設定、エミュレータのダウンロード、Firebase の Flutter アプリへの追加を、わずか数個のターミナル コマンドで行うことができます。

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

firebase init

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

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

f11dcab439e6ac1e.png

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

3bdc0c6934991c25.png

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

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

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

8544e41037637b07.png

FlutterFire を構成する

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

flutterfire configure

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

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

619b7aca6dc15472.png 301c9534f594f472.png

このスクリーンショットは、プロセスの最後に表示される出力を示しています。Firebase に精通している方は、コンソールでアプリケーションを作成する必要がなく、FlutterFire CLI が自動的に作成したことに気づくでしょう。

12199a85ade30459.png

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

設定の最後の手順は、関連する 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 プロジェクトはエミュレータを使用できるように設定されていますが、Flutter コードに送信 Firebase リクエストをローカルポートに転送するように指示する必要があります。

まず、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] カードで [Go to emulator] をクリックして、Auth エミュレータを起動します。次のようなページが表示されます。

3c1bfded40733189.png

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

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

ユーザーを追加する

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

  • 表示名: Dash
  • メール: dash@email.com
  • パスワード: dashword

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

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 文字列が認証エミュレータで作成したメールアドレスとパスワードに置き換えられます。次の行では、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 クラスは Firebase Auth から提供され、User.displayName などの必要な情報を提供します。これについては後で説明します。

これは、Firebase Auth でメールアドレスとパスワードを使用してユーザーをログインさせるために必要な基本的なコードです。FirebaseAuth を呼び出してログインし、Future<UserCredential> オブジェクトを返します。このコードは、フューチャーが完了すると、UserCredentialUser が関連付けられているかどうかを確認します。認証情報オブジェクトにユーザーが存在する場合、ユーザーは正常にログインしており、AppState.user プロパティを設定できます。ない場合はエラーが発生し、エラーがプリントされます。

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

TODO: アクション アイコン - アプリを再読み込みし、レンダリングされたら [ログイン] ボタンを押します。これにより、アプリは上部に「おかえりなさい、ユーザー様」と表示されたページに移動します。認証は機能しているため、このページに移動できますが、ユーザーの実際の名前を表示するには、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 によって作成されました。

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

フォームの [送信] を押します。アプリには何も起こりませんが、エミュレータの UI で新しいエントリを確認できます。

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

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

a978fb34fb8a83da.png

これで AppState.writeEntryToFirestore が機能したことが確認できました。次に、[リクエスト] タブでリクエストの詳細を確認します。そのタブをクリックします。

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

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

f0b37f0341639035.png

リスト内の項目をクリックすると、さまざまな有用な情報を確認できます。リクエストに対応する CREATE リストアイテムをクリックして、新しいジャーナル エントリを作成します。次のような新しい表が表示されます。

385d62152e99aad4.png

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

また、メタデータや認証データなど、このリクエストのすべての部分を簡単に検査する方法も提供します。このデータは、複雑な承認ルールを作成するために使用されます。

Firestore からの読み取り

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

このコードは、それぞれ AppState._entriesStreamControllerAppState.entries と呼ばれる StreamControllerStream と連携して動作します。このコードはすでに作成されています。Firestore からデータを表示するために UI で必要なコードもすべて作成されています。

_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 という StreamController に追加されます(UI はこれをリッスンしています)。このコードが唯一必要な更新です。

最後に、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 エミュレータ
  • エミュレータ データのエクスポートとインポート

次のステップ

詳細

Sparky はあなたを誇りに思っています。

2a0ad195769368b1.gif