FlutterのFirebaseについて知る

1.始める前に

このコードラボでは、あなたはの基礎のいくつかを学びますFirebaseをAndroidとiOSのためのフラッターのモバイルアプリケーションを作成します。

前提条件

このコードラボでは、あなたがフラッターに精通していると仮定し、あなたがインストールしたフラッターSDK 、およびエディタを

作成するもの

このコードラボでは、Flutterを使用して、Android、iOS、Web、およびmacOSでイベントRSVPおよびゲストブックチャットアプリを構築します。 Firebase認証でユーザーを認証し、CloudFirestoreを使用してデータを同期します。

必要なもの

このコードラボは、次のデバイスのいずれかを使用して実行できます。

上記に加えて、次のものも必要になります。

  • Chromeなどの選択したブラウザ。
  • アンIDEかなど、お好みのテキストエディタ、 AndroidのメーカーまたはVSコードダーツ・フラッターのプラグインで構成。
  • 最新stableバージョンのフラッター(またはbetaあなたは、エッジで生活を楽しむ場合)。
  • Firebaseプロジェクトを作成および管理するためのGmailアカウントなどのGoogleアカウント。
  • firebaseコマンドラインツールは、Gmailアカウントにログインしています。
  • codelabのサンプルコード。コードを取得する方法については、次の手順を参照してください。

2.サンプルコードを取得します

GitHubからプロジェクトの初期バージョンをダウンロードすることから始めましょう。

クローンGitHubのリポジトリ、コマンドラインから:

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

また、あなたが持っている場合はGitHubでのCLIツールをインストール:

gh repo clone flutter/codelabs flutter-codelabs

サンプルコードでは、中にクローン化されなければならないflutter-codelabs codelabsの収集のためのコードを含むディレクトリ、。このコードラボのコードはであるflutter-codelabs/firebase-get-to-know-flutter

下のディレクトリ構造flutter-codelabs/firebase-get-to-know-flutterあなたが各名前付きステップの最後であるべきところの一連のスナップショットです。これはステップ2であるため、一致するファイルを見つけるのは次のように簡単です。

cd flutter-codelabs/firebase-get-to-know-flutter/step_02

先にスキップしたい場合、またはステップの後に何かがどのように見えるかを確認したい場合は、関心のあるステップにちなんで名付けられたディレクトリを調べてください。

スターターアプリをインポートする

オープンは、インポートまたはflutter-codelabs/firebase-get-to-know-flutter/step_02お好みのIDEにディレクトリ。このディレクトリには、まだ機能していないFlutterミートアップアプリで構成されるcodelabの開始コードが含まれています。

作業するファイルを見つけます

このアプリのコードは複数のディレクトリに分散しています。この機能の分割は、コードを機能ごとにグループ化することにより、作業を容易にするように設計されています。

プロジェクトで次のファイルを見つけます。

  • lib/main.dart :このファイルは、メインエントリポイントとアプリケーションのウィジェットが含まれています。
  • lib/src/widgets.dart :このファイルは、アプリケーションのスタイリングを標準化する助けにウィジェットの一握りが含まれています。これらは、スターターアプリの画面を構成するために使用されます。
  • lib/src/authentication.dart :このファイルは、の部分的な実装が含まれFirebaseUI認証をFirebase電子メールベースの認証用のログイン・ユーザーエクスペリエンスを作成するためのウィジェットのセットで。認証フロー用のこれらのウィジェットは、スターターアプリではまだ使用されていませんが、すぐに接続します。

アプリケーションの残りの部分を構築するために、必要に応じてファイルを追加します。

見直しlib/main.dartファイルを

このアプリはを活用するgoogle_fonts Roboto全アプリ全体のデフォルトフォントにするために私たちを可能にするためのパッケージ。やる気の読者のための運動が探ることであるfonts.google.comを、あなたはアプリのさまざまな部分であり発見フォントを使用しています。

あなたはからヘルパーウィジェットを利用しているlib/src/widgets.dartの形でHeaderParagraphIconAndDetail 。これらのウィジェットはで説明したページレイアウトに混乱を減らすHomePage重複したコードを排除することによって。これには、一貫したルックアンドフィールを可能にするという追加の利点があります。

Android、iOS、Web、macOSでのアプリの外観は次のとおりです。

アプリのプレビュー

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

イベント情報を表示することはゲストにとっては素晴らしいことですが、イベントを表示するだけでは誰にとってもあまり役に立ちません。このアプリにいくつかの動的機能を追加しましょう。このためには、Firebaseをアプリに接続する必要があります。 Firebaseの使用を開始するには、Firebaseプロジェクトを作成して設定する必要があります。

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

  1. サインインしFirebase
  2. Firebaseコンソールで、クリックプロジェクトを追加(またはプロジェクトを作成します)、そしてあなたのFirebaseプロジェクトFirebaseフラッタ・コードラボに名前付けます。

4395e4e67c08043a.png

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

b7138cde5f2c7b61.png

Firebaseプロジェクトの詳細については、参照Firebaseプロジェクトを理解します

作成しているアプリは、ウェブアプリで利用できるいくつかのFirebase製品を使用しています。

  • Firebase認証は、ユーザーが自分のアプリに署名することを可能にします。
  • クラウド上の構造化データを保存したときにデータの変更のインスタント通知を取得するためのクラウドFirestore。
  • Firebaseセキュリティルールは、データベースを確保します。

これらの製品の一部は、特別な構成が必要であるか、Firebaseコンソールを使用して有効にする必要があります。

電子メールのサインインFirebase認証のための有効化

ユーザーがウェブアプリにログインできるようにするには、このコードラボのために電子メール/パスワードログインメソッドを使用します:

  1. Firebaseコンソールでは、左側のパネルで、[ビルド]メニューを展開します。
  2. 認証]クリックし、[取得の開始]ボタンをクリックし、サインインし、メソッドタブ(またはこちらをクリックしてくださいサインインのメソッドタブに直接移動します)。
  3. 位置でのスイッチをオンにし、[保存]クリックし、プロバイダ・サインインリストに電子メール/パスワード]クリックします。 58e3e3e23c2f16a4.png

CloudFirestoreを有効にする

ウェブアプリが使用するクラウドFirestoreをチャットメッセージを保存し、新しいチャットメッセージを受信します。

CloudFirestoreを有効にする:

  1. Firebaseコンソールの[ビルド]セクションでは、クラウドFirestoreをクリックします。
  2. データベースの作成]クリックします。 99e8429832d23fa3.png
  1. テストモードオプションの[開始]を選択します。セキュリティルールに関する免責事項をお読みください。テストモードでは、開発中にデータベースに自由に書き込むことができます。 [次へ]クリックします。 6be00e26c72ea032.png
  1. データベースの場所を選択します(デフォルトを使用できます)。この場所は後で変更できないことに注意してください。 278656eefcfb0216.png
  2. 有効にする]をクリックします。

4.Firebaseの構成

FlutterでFirebaseを使用するには、FlutterFireライブラリを正しく利用するようにFlutterプロジェクトを構成するプロセスに従う必要があります。

  • FlutterFireの依存関係をプロジェクトに追加します
  • 目的のプラットフォームをFirebaseプロジェクトに登録します
  • プラットフォーム固有の構成ファイルをダウンロードして、コードに追加します。

あなたのフラッタアプリのトップレベルのディレクトリでは、と呼ばれるサブディレクトリがあるandroidiosmacosweb 。これらのディレクトリは、それぞれiOSとAndroidのプラットフォーム固有の構成ファイルを保持します。

依存関係を構成する

このアプリで使用している2つのFirebase製品(FirebaseAuthとCloudFirestore)のFlutterFireライブラリを追加する必要があります。次の3つのコマンドを実行して、依存関係を追加します。

$ flutter pub add firebase_core 
Resolving dependencies...
+ firebase_core 1.10.5
+ firebase_core_platform_interface 4.2.2
+ firebase_core_web 1.5.2
+ flutter_web_plugins 0.0.0 from sdk flutter
+ js 0.6.3
  test_api 0.4.3 (0.4.8 available)
Changed 5 dependencies!

firebase_coreすべてFirebaseフラッタープラグインに必要な共通のコードです。

$ flutter pub add firebase_auth
Resolving dependencies...
+ firebase_auth 3.3.3
+ firebase_auth_platform_interface 6.1.8
+ firebase_auth_web 3.3.4
+ intl 0.17.0
  test_api 0.4.3 (0.4.8 available)
Changed 4 dependencies!

firebase_auth Firebaseの認証機能との統合を可能にします。

$ flutter pub add cloud_firestore
Resolving dependencies...
+ cloud_firestore 3.1.4
+ cloud_firestore_platform_interface 5.4.9
+ cloud_firestore_web 2.6.4
  test_api 0.4.3 (0.4.8 available)
Changed 3 dependencies!

cloud_firestoreクラウドFirestoreデータストレージへのアクセスを可能にします。

$ flutter pub add provider
Resolving dependencies...
+ nested 1.0.0
+ provider 6.0.1
  test_api 0.4.3 (0.4.8 available)
Changed 2 dependencies!

必要なパッケージを追加すると同時に、Firebaseを適切に利用するようにiOS、Android、macOS、およびWebランナープロジェクトを構成する必要があります。また、使用しているprovider表示ロジックからビジネスロジックの分離を可能にするパッケージを。

インストールflutterfire

  1. FlutterFire CLIは、基盤となるFirebaseCLIに依存します。あなたはまだ行っていない場合は、最新バージョンのインストールまたはアップデートFirebase CLI
  2. :次は、次のコマンドを実行してFlutterFire CLIをインストール
    $ dart pub global activate flutterfire_cli
    

インストール後、 flutterfireコマンドは、グローバルに利用できるようになります。

アプリの構成

CLIは、Firebaseプロジェクトと選択したプロジェクトアプリケーションから情報を抽出して、特定のプラットフォームのすべての構成を生成します。

アプリケーションのルートで、configureコマンドを実行します。

$ flutterfire configure

構成コマンドは、いくつかのプロセスをガイドします。

  1. Firebaseプロジェクトの選択(.firebasercファイルまたはFirebaseコンソールから)。
  2. 構成するプラットフォーム(Android、iOS、macOS、Webなど)を入力します。
  3. 選択したプラットフォームのどのFirebaseアプリケーションを使用して構成を抽出する必要があるかを特定します。デフォルトでは、CLIは現在のプロジェクト構成に基づいてFirebaseアプリを自動的に照合しようとします。
  4. プロジェクトでfirebase_options.dartファイルを生成します。

macOSを構成する

macOSのFlutterは、完全にサンドボックス化されたアプリケーションを構築します。このアプリケーションはネットワークを使用してFirebaseサーバーと通信するために統合されているため、ネットワーククライアント権限を使用してアプリケーションを構成する必要があります。

macos / Runner / DebugProfile.entitlements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.cs.allow-jit</key>
	<true/>
	<key>com.apple.security.network.server</key>
	<true/>
  <!-- Add the following two lines -->
	<key>com.apple.security.network.client</key>
	<true/>
</dict>
</plist>

macos / Runner / Release.entitlements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
  <!-- Add the following two lines -->
	<key>com.apple.security.network.client</key>
	<true/>
</dict>
</plist>

参照の資格とアプリのサンドボックスの詳細については。

5.ユーザーサインイン(RSVP)を追加します

今、あなたはアプリにFirebaseを追加したことを、あなたはレジスタの人々が使用していることをRSVPボタンに設定することができますFirebase認証を。 Androidネイティブ、iOSネイティブ、およびWebの場合、事前にビルドされたFirebaseUI Authパッケージがありますが、Flutterの場合、この機能をビルドする必要があります。

手順2で取得したプロジェクトには、ほとんどの認証フローのユーザーインターフェイスを実装する一連のウィジェットが含まれていました。 FirebaseAuthenticationをアプリケーションに統合するためのビジネスロジックを実装します。

プロバイダーとのビジネスロジック

あなたは、使用しようとしているproviderフラッタウィジェットのアプリケーションのツリー全体で集中型のアプリケーション状態オブジェクトを利用できるようにパッケージを。で開始するには、の上部に輸入を変更するlib/main.dart

lib / main.dart

import 'package:firebase_auth/firebase_auth.dart'; // new
import 'package:firebase_core/firebase_core.dart'; // new
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';           // new

import 'firebase_options.dart';                    // new
import 'src/authentication.dart';                  // new
import 'src/widgets.dart';

importラインはFirebase Coreと認証、プル紹介providerウィジェットツリーを介してアプリケーション状態オブジェクトを利用できるように、との認証ウィジェット含めるために使用しているパッケージlib/src

このアプリケーション状態オブジェクト、 ApplicationState 、このステップのための2つの主要な責任を持っていますが、それ以降の手順でアプリケーションに多くの機能を追加すると、追加の責任を得ることができます。最初の責任はを呼び出してFirebaseライブラリを初期化することであるFirebase.initializeApp()した後、承認フローの取り扱いがあります。最後に次のクラスを追加しますlib/main.dart

lib / main.dart

class ApplicationState extends ChangeNotifier {
  ApplicationState() {
    init();
  }

  Future<void> init() async {
    await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
    );

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
      } else {
        _loginState = ApplicationLoginState.loggedOut;
      }
      notifyListeners();
    });
  }

  ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
  ApplicationLoginState get loginState => _loginState;

  String? _email;
  String? get email => _email;

  void startLoginFlow() {
    _loginState = ApplicationLoginState.emailAddress;
    notifyListeners();
  }

  Future<void> verifyEmail(
    String email,
    void Function(FirebaseAuthException e) errorCallback,
  ) async {
    try {
      var methods =
          await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
      if (methods.contains('password')) {
        _loginState = ApplicationLoginState.password;
      } else {
        _loginState = ApplicationLoginState.register;
      }
      _email = email;
      notifyListeners();
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  Future<void> signInWithEmailAndPassword(
    String email,
    String password,
    void Function(FirebaseAuthException e) errorCallback,
  ) async {
    try {
      await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void cancelRegistration() {
    _loginState = ApplicationLoginState.emailAddress;
    notifyListeners();
  }

  Future<void> registerAccount(
      String email,
      String displayName,
      String password,
      void Function(FirebaseAuthException e) errorCallback) async {
    try {
      var credential = await FirebaseAuth.instance
          .createUserWithEmailAndPassword(email: email, password: password);
      await credential.user!.updateDisplayName(displayName);
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void signOut() {
    FirebaseAuth.instance.signOut();
  }
}

このクラスのいくつかの重要なポイントに注意する価値があります。ユーザーは認証されていない状態で開始し、アプリはユーザーのメールアドレスをリクエストするフォームを表示します。そのメールアドレスが登録されているかどうかに応じて、アプリはユーザー登録を求めるか、パスワードをリクエストします。その後、すべてがうまくいくと仮定すると、ユーザーは認証されます。

これは、FirebaseUI Authフローの完全な実装ではないことに注意する必要があります。これは、ログインに問題がある既存のアカウントを持つユーザーのケースを処理しないためです。この追加機能の実装は、演習として残されています。やる気のある読者。

認証フローの統合

今、あなたは、アプリケーションの状態の開始を持っていること、アプリの初期化にアプリケーションの状態を配線し、に認証フローを追加するための時間であるHomePage 。経由してアプリケーションの状態を統合するメインエントリポイントを更新providerパッケージ:

lib / main.dart

void main() {
  // Modify from here
  runApp(
    ChangeNotifierProvider(
      create: (context) => ApplicationState(),
      builder: (context, _) => App(),
    ),
  );
  // to here.
}

変更main機能は、使用してアプリケーションの状態オブジェクトをインスタンス化する責任プロバイダパッケージになりChangeNotifierProviderウィジェットを。アプリケーション状態オブジェクトが伸びるため、この特定のプロバイダクラスを使用しているChangeNotifierして、これが有効にproviderに依存するウィジェットを再表示する際に知ってパッケージを。最後に、使用してアプリケーションの状態の統合Authentication更新することにより、 HomePagebuild方法を:

lib / main.dart

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Firebase Meetup'),
      ),
      body: ListView(
        children: <Widget>[
          Image.asset('assets/codelab.png'),
          const SizedBox(height: 8),
          const IconAndDetail(Icons.calendar_today, 'October 30'),
          const IconAndDetail(Icons.location_city, 'San Francisco'),
          // Add from here
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Authentication(
              email: appState.email,
              loginState: appState.loginState,
              startLoginFlow: appState.startLoginFlow,
              verifyEmail: appState.verifyEmail,
              signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
              cancelRegistration: appState.cancelRegistration,
              registerAccount: appState.registerAccount,
              signOut: appState.signOut,
            ),
          ),
          // to here
          const Divider(
            height: 8,
            thickness: 1,
            indent: 8,
            endIndent: 8,
            color: Colors.grey,
          ),
          const Header("What we'll be doing"),
          const Paragraph(
            'Join us for a day full of Firebase Workshops and Pizza!',
          ),
        ],
      ),
    );
  }
}

あなたは、インスタンスAuthenticationウィジェットを、そして、それをラップConsumerウィジェット。消費者はそれを通常の方法をウィジェットproviderパッケージはツリーアプリケーションの状態の変化の一部を再構築するために使用することができます。 Authenticationあなたが今テストすることを認証UIウィジェットです。

認証フローのテスト

cdf2d25e436bd48d.png

これが認証フローの開始です。ここで、ユーザーはRSVPボタンをタップして、電子メールフォームを開始できます。

2a2cd6d69d172369.png

電子メールを入力すると、システムはユーザーがすでに登録されているかどうかを確認します。登録されている場合はパスワードの入力を求められます。登録されていない場合は、登録フォームを使用します。

e5e65065dba36b54.png

エラー処理フローを確認するには、必ず短いパスワード(6文字未満)を入力してみてください。ユーザーが登録されている場合は、代わりにのパスワードが表示されます。

fbb3ea35fb4f67a.png

このページでは、このページのエラー処理を確認するために、間違ったパスワードを入力してください。最後に、ユーザーがログインすると、ユーザーに再度ログアウトする機能を提供するログインエクスペリエンスが表示されます。

4ed811a25b0cf816.png

これで、認証フローが実装されました。おめでとうございます!

6. CloudFirestoreにメッセージを書き込みます

ユーザーが来ていることを知っているのは素晴らしいことですが、アプリでゲストに何か他のことをしてもらいましょう。ゲストブックにメッセージを残すことができたらどうでしょうか。彼らは、なぜ彼らが来ることに興奮しているのか、誰に会いたいのかを共有することができます。

ユーザーがアプリで書くチャットメッセージを格納するには、使用しますクラウドFirestoreを

データ・モデル

Cloud FirestoreはNoSQLデータベースであり、データベースに格納されているデータは、コレクション、ドキュメント、フィールド、およびサブコレクションに分割されます。あなたはと呼ばれるトップレベルのコレクション内の文書としてチャットの各メッセージを格納しますguestbook

7c20dc8424bb1d84.png

Firestoreにメッセージを追加する

このセクションでは、ユーザーがデータベースに新しいメッセージを書き込むための機能を追加します。まず、UI要素(フォームフィールドと送信ボタン)を追加し、次にこれらの要素をデータベースに接続するコードを追加します。

まず、輸入のための追加cloud_firestoreパッケージとdart:async

lib / main.dart

import 'dart:async';                                    // new

import 'package:cloud_firestore/cloud_firestore.dart';  // new
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';

import 'firebase_options.dart';
import 'src/authentication.dart';
import 'src/widgets.dart';

メッセージフィールドと送信ボタンのUI要素を構築するには、ウィジェットの新しいステートフル追加GuestBookの下部にあるlib/main.dart

lib / main.dart

class GuestBook extends StatefulWidget {
  const GuestBook({required this.addMessage});
  final FutureOr<void> Function(String message) addMessage;

  @override
  _GuestBookState createState() => _GuestBookState();
}

class _GuestBookState extends State<GuestBook> {
  final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
  final _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Form(
        key: _formKey,
        child: Row(
          children: [
            Expanded(
              child: TextFormField(
                controller: _controller,
                decoration: const InputDecoration(
                  hintText: 'Leave a message',
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Enter your message to continue';
                  }
                  return null;
                },
              ),
            ),
            const SizedBox(width: 8),
            StyledButton(
              onPressed: () async {
                if (_formKey.currentState!.validate()) {
                  await widget.addMessage(_controller.text);
                  _controller.clear();
                }
              },
              child: Row(
                children: const [
                  Icon(Icons.send),
                  SizedBox(width: 4),
                  Text('SEND'),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

ここにはいくつかの興味深い点があります。まず、フォームをインスタンス化して、upiがメッセージに実際にコンテンツが含まれていることを検証し、コンテンツがない場合はユーザーにエラーメッセージを表示できるようにします。フォームを検証する方法は、フォームの後ろに、フォームの状態にアクセスすることを含む、これのためにあなたが使用GlobalKey 。キーの詳細については、それらを使用する方法を参照してください「使うキーに」フラッターウィジェット101のエピソードを

また、ウィジェットがレイアウトされている方法に注意してください、あなたが持っているRowして、 TextFormFieldStyledButton自体が含まれている、 Row 。また、注意してくださいTextFormFieldに包まれているExpandedウィジェット、この力TextFormField行の任意の余分なスペースを取るします。これが必要な理由をよりよく理解するために、一読してください理解制約

これで、ユーザーがゲストブックに追加するテキストを入力できるウィジェットができたので、それを画面に表示する必要があります。これを行うには、編集の身体HomePageの一番下に次の2行を追加するにはListViewの子:

const Header("What we'll be doing"),
const Paragraph(
  'Join us for a day full of Firebase Workshops and Pizza!',
),
// Add the following two lines.
const Header('Discussion'),
GuestBook(addMessage: (message) => print(message)),

これはウィジェットを表示するのに十分ですが、何か便利なことをするのに十分ではありません。このコードをまもなく更新して、機能させる予定です。

アプリのプレビュー

SENDボタンをクリックすると、ユーザーは以下のコードをトリガーします。これは、メッセージの入力フィールドの内容を追加してguestbookデータベースのコレクション。具体的には、 addMessageToGuestBook方法は、に(自動的に生成されたIDを持つ)新しい文書にメッセージの内容を追加guestbookコレクション。

注意FirebaseAuth.instance.currentUser.uid Firebase認証がログインしているユーザーのすべてのために与えることが自動生成されたユニークなIDへの参照です。

別の変更作るlib/main.dartファイルを。追加addMessageToGuestBook方法を。次のステップでは、ユーザーインターフェイスとこの機能を一緒に配線します。

lib / main.dart

class ApplicationState extends ChangeNotifier {

  // Current content of ApplicationState elided ...

  // Add from here
  Future<DocumentReference> addMessageToGuestBook(String message) {
    if (_loginState != ApplicationLoginState.loggedIn) {
      throw Exception('Must be logged in');
    }

    return FirebaseFirestore.instance
        .collection('guestbook')
        .add(<String, dynamic>{
      'text': message,
      'timestamp': DateTime.now().millisecondsSinceEpoch,
      'name': FirebaseAuth.instance.currentUser!.displayName,
      'userId': FirebaseAuth.instance.currentUser!.uid,
    });
  }
  // To here
}

UIをデータベースに配線する

ユーザーがゲストブックに追加するテキストを入力できるUIがあり、CloudFirestoreにエントリを追加するためのコードがあります。今、あなたがする必要があるのは、2つを一緒に配線することです。 lib/main.dartを次のように変更しますHomePageウィジェットを。

lib / main.dart

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Firebase Meetup'),
      ),
      body: ListView(
        children: <Widget>[
          Image.asset('assets/codelab.png'),
          const SizedBox(height: 8),
          const IconAndDetail(Icons.calendar_today, 'October 30'),
          const IconAndDetail(Icons.location_city, 'San Francisco'),
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Authentication(
              email: appState.email,
              loginState: appState.loginState,
              startLoginFlow: appState.startLoginFlow,
              verifyEmail: appState.verifyEmail,
              signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
              cancelRegistration: appState.cancelRegistration,
              registerAccount: appState.registerAccount,
              signOut: appState.signOut,
            ),
          ),
          const Divider(
            height: 8,
            thickness: 1,
            indent: 8,
            endIndent: 8,
            color: Colors.grey,
          ),
          const Header("What we'll be doing"),
          const Paragraph(
            'Join us for a day full of Firebase Workshops and Pizza!',
          ),
          // Modify from here
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (appState.loginState == ApplicationLoginState.loggedIn) ...[
                  const Header('Discussion'),
                  GuestBook(
                    addMessage: (message) =>
                        appState.addMessageToGuestBook(message),
                  ),
                ],
              ],
            ),
          ),
          // To here.
        ],
      ),
    );
  }
}

このステップの開始時に追加した2行を、完全な実装に置き換えました。あなたは再び使用しているConsumer<ApplicationState>あなたがレンダリングされているツリーの一部へのアプリケーションの状態を利用できるようにします。これにより、UIにメッセージを入力した人に反応し、それをデータベースに公開できます。次のセクションでは、追加されたメッセージがデータベースに公開されているかどうかをテストします。

メッセージの送信をテストする

  1. アプリにサインインしていることを確認してください。
  2. そのような「ちょっとそこ!」とメッセージを入力し、[送信]クリックしてください。

このアクションにより、メッセージがCloudFirestoreデータベースに書き込まれます。ただし、データの取得を実装する必要があるため、実際のFlutterアプリにはまだメッセージが表示されません。次のステップでそれを行います。

ただし、Firebaseコンソールで新しく追加されたメッセージを確認できます。

Firebaseコンソールで、中にデータベースのダッシュボード、あなたが見るべきであるguestbookあなたの新しく追加されたメッセージのコレクションを。メッセージを送信し続けると、ゲストブックコレクションには次のような多くのドキュメントが含まれます。

Firebaseコンソール

713870af0b3b63c.png

7.メッセージを読む

ゲストがデータベースにメッセージを書き込むことができるのは素晴らしいことですが、アプリではまだメッセージを見ることができません。それを直そう!

メッセージを同期する

メッセージを表示するには、データが変更されたときにトリガーするリスナーを追加してから、新しいメッセージを表示するUI要素を作成する必要があります。アプリから新しく追加されたメッセージをリッスンするコードをアプリケーションの状態に追加します。

ただ、上記のGuestBook次の値クラスをウィジェット。このクラスは、CloudFirestoreに保存しているデータの構造化されたビューを公開します。

lib / main.dart

class GuestBookMessage {
  GuestBookMessage({required this.name, required this.message});
  final String name;
  final String message;
}

セクションではApplicationStateあなたは状態とゲッターを定義し、次の新しい行を追加します。

lib / main.dart

  ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
  ApplicationLoginState get loginState => _loginState;

  String? _email;
  String? get email => _email;

  // Add from here
  StreamSubscription<QuerySnapshot>? _guestBookSubscription;
  List<GuestBookMessage> _guestBookMessages = [];
  List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
  // to here.

そして最後に、初期化のセクションでApplicationState 、ときにユーザーがログインする文書のコレクションをクエリに加入し、彼らはログアウト時に解除するには、次のように追加します。

lib / main.dart

  Future<void> init() async {
    await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
    );

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        // Add from here
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          for (final document in snapshot.docs) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'] as String,
                message: document.data()['text'] as String,
              ),
            );
          }
          notifyListeners();
        });
        // to here.
      } else {
        _loginState = ApplicationLoginState.loggedOut;
        // Add from here
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        // to here.
      }
      notifyListeners();
    });
  }

このセクションでは、のように、ここで、重要であるあなたがオーバークエリ構築ところでguestbookコレクションを、ハンドルがサブスクライブし、このコレクションに退会します。あなたは、あなたがメッセージのローカルキャッシュを再構築ストリーム、に耳を傾けるguestbookコレクション、そしてあなたが後でそれから退会することができますので、またこのサブスクリプションへの参照を格納します。ここでは多くのことが行われているので、より明確なメンタルモデルを取得するときに何が起こるかを調べるためにデバッガーで時間を費やす価値があります。

詳細については、クラウドFirestoreのマニュアルを

ではGuestBookユーザーインターフェイスに、この変化状態を接続する必要がウィジェット。ウィジェットの構成の一部としてメッセージのリストを追加することにより、ウィジェットを変更します。

lib / main.dart

class GuestBook extends StatefulWidget {
  // Modify the following line
  const GuestBook({required this.addMessage, required this.messages});
  final FutureOr<void> Function(String message) addMessage;
  final List<GuestBookMessage> messages; // new

  @override
  _GuestBookState createState() => _GuestBookState();
}

次に、我々はこの新しい設定公開_GuestBookState変更することでbuild 、次のような方法を。

lib / main.dart

class _GuestBookState extends State<GuestBook> {
  final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
  final _controller = TextEditingController();

  @override
  // Modify from here
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // to here.
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Form(
            key: _formKey,
            child: Row(
              children: [
                Expanded(
                  child: TextFormField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      hintText: 'Leave a message',
                    ),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'Enter your message to continue';
                      }
                      return null;
                    },
                  ),
                ),
                const SizedBox(width: 8),
                StyledButton(
                  onPressed: () async {
                    if (_formKey.currentState!.validate()) {
                      await widget.addMessage(_controller.text);
                      _controller.clear();
                    }
                  },
                  child: Row(
                    children: const [
                      Icon(Icons.send),
                      SizedBox(width: 4),
                      Text('SEND'),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
        // Modify from here
        const SizedBox(height: 8),
        for (var message in widget.messages)
          Paragraph('${message.name}: ${message.message}'),
        const SizedBox(height: 8),
      ],
      // to here.
    );
  }
}

あなたはとビルド方法の以前の内容をラップColumnウィジェット、その後の最後尾にColumnの子、あなたは追加のためのコレクションを新たに生成するために、 Paragraphのメッセージのリスト内の各メッセージのために。

最後に、あなたは今の身体に更新する必要がHomePage正しく構築するためにGuestBook新たにしてmessagesパラメータ。

lib / main.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      if (appState.loginState == ApplicationLoginState.loggedIn) ...[
        const Header('Discussion'),
        GuestBook(
          addMessage: (message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages, // new
        ),
      ],
    ],
  ),
),

同期メッセージをテストする

Cloud Firestoreは、データベースにサブスクライブしているクライアントとデータを自動的かつ即座に同期します。

  1. データベースで以前に作成したメッセージは、アプリに表示されます。新しいメッセージを自由に書いてください。それらはすぐに表示されるはずです。
  2. ワークスペースを複数のウィンドウまたはタブで開くと、メッセージはタブ間でリアルタイムに同期されます。
  3. (オプション)は、削除、変更、またはFirebaseコンソールのデータベースのセクションに直接新しいメッセージを追加し、手動で試すことができます。変更はUIに表示されます。

おめでとう!アプリでCloudFirestoreドキュメントを読んでいます!

アプリケーションのpレビュー

8.基本的なセキュリティルールを設定します

最初に、テストモードを使用するようにCloud Firestoreを設定しました。これは、データベースが読み取りと書き込みのために開いていることを意味します。ただし、テストモードは、開発のごく初期の段階でのみ使用する必要があります。ベストプラクティスとして、アプリを開発するときにデータベースのセキュリティルールを設定する必要があります。セキュリティは、アプリの構造と動作に不可欠である必要があります。

セキュリティルールを使用すると、データベース内のドキュメントとコレクションへのアクセスを制御できます。柔軟なルール構文を使用すると、データベース全体へのすべての書き込みから特定のドキュメントの操作まで、あらゆるものに一致するルールを作成できます。

CloudFirestoreのセキュリティルールはFirebaseコンソールで作成できます。

  1. Firebaseコンソールの開発セクションでは、データベースをクリックし、[ルール]タブを選択します(またはこちらをクリックしてくださいルール]タブに直接移動します)。
  2. 次のデフォルトのセキュリティルールと、公開されているルールに関する警告が表示されます。

7767a2d2e64e7275.png

コレクションを特定する

まず、アプリがデータを書き込むコレクションを特定します。

match /databases/{database}/documents 、保護したいというコレクションを識別します。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
     // You'll add rules here in the next step.
  }
}

セキュリティルールを追加する

各ゲストブックドキュメントのフィールドとして認証UIDを使用したため、認証UIDを取得して、ドキュメントに書き込もうとしているユーザーが一致する認証UIDを持っていることを確認できます。

以下に示すように、読み取りルールと書き込みルールをルールセットに追加します。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
      allow read: if request.auth.uid != null;
      allow write:
        if request.auth.uid == request.resource.data.userId;
    }
  }
}

これで、ゲストブックの場合、サインインしたユーザーのみがメッセージを読むことができます(任意のメッセージ!)が、メッセージの作成者のみがメッセージを編集できます。

検証ルールを追加する

データ検証を追加して、予想されるすべてのフィールドがドキュメントに存在することを確認します。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
      allow read: if request.auth.uid != null;
      allow write:
      if request.auth.uid == request.resource.data.userId
          && "name" in request.resource.data
          && "text" in request.resource.data
          && "timestamp" in request.resource.data;
    }
  }
}

9.ボーナスステップ:学んだことを練習する

出席者の出欠確認ステータスを記録する

現在、このアプリでは、イベントに興味がある場合にチャットを開始できます。また、誰かが来ているかどうかを知る唯一の方法は、チャットに投稿するかどうかです。整理して、何人の人が来るのかを知らせましょう。

アプリケーションの状態にいくつかの新機能を追加します。 1つ目は、ログインしているユーザーが参加しているかどうかを指定する機能です。 2番目の機能は、実際に参加している人数のカウンターです。

lib/main.dart 、この状態と対話するためにUIコードを有効にするには、アクセサセクションに次の行を追加します。

lib / main.dart

int _attendees = 0;
int get attendees => _attendees;

Attending _attending = Attending.unknown;
StreamSubscription<DocumentSnapshot>? _attendingSubscription;
Attending get attending => _attending;
set attending(Attending attending) {
  final userDoc = FirebaseFirestore.instance
      .collection('attendees')
      .doc(FirebaseAuth.instance.currentUser!.uid);
  if (attending == Attending.yes) {
    userDoc.set(<String, dynamic>{'attending': true});
  } else {
    userDoc.set(<String, dynamic>{'attending': false});
  }
}

更新ApplicationStateさんinitメソッドを次のように

lib / main.dart

  Future<void> init() async {
    await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
    );

    // Add from here
    FirebaseFirestore.instance
        .collection('attendees')
        .where('attending', isEqualTo: true)
        .snapshots()
        .listen((snapshot) {
      _attendees = snapshot.docs.length;
      notifyListeners();
    });
    // To here

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          for (final document in snapshot.docs) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'] as String,
                message: document.data()['text'] as String,
              ),
            );
          }
          notifyListeners();
        });
        // Add from here
        _attendingSubscription = FirebaseFirestore.instance
            .collection('attendees')
            .doc(user.uid)
            .snapshots()
            .listen((snapshot) {
          if (snapshot.data() != null) {
            if (snapshot.data()!['attending'] as bool) {
              _attending = Attending.yes;
            } else {
              _attending = Attending.no;
            }
          } else {
            _attending = Attending.unknown;
          }
          notifyListeners();
        });
        // to here
      } else {
        _loginState = ApplicationLoginState.loggedOut;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        _attendingSubscription?.cancel(); // new
      }
      notifyListeners();
    });
  }

上記は、出席者の数を調べるために常にサブスクライブされたクエリと、ユーザーが出席しているかどうかを調べるためにユーザーがログインしている間だけアクティブになる2番目のクエリを追加します。次に、後に次の列挙を追加しGuestBookMessage宣言:

lib / main.dart

enum Attending { yes, no, unknown }

次に、古いラジオボタンのように機能する新しいウィジェットを定義します。はいもいいえも選択されていない不確定な状態で始まりますが、ユーザーが出席するかどうかを選択すると、そのオプションが塗りつぶされたボタンで強調表示され、他のオプションがフラットレンダリングで後退します。

lib / main.dart

class YesNoSelection extends StatelessWidget {
  const YesNoSelection({required this.state, required this.onSelection});
  final Attending state;
  final void Function(Attending selection) onSelection;

  @override
  Widget build(BuildContext context) {
    switch (state) {
      case Attending.yes:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              ElevatedButton(
                style: ElevatedButton.styleFrom(elevation: 0),
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              TextButton(
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
      case Attending.no:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              TextButton(
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              ElevatedButton(
                style: ElevatedButton.styleFrom(elevation: 0),
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
      default:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              StyledButton(
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              StyledButton(
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
    }
  }
}

次は、あなたが更新する必要がHomePageを利用することのビルド方法をYesNoSelection彼らが参加している場合は指名するユーザーでログイン可能に。このイベントの参加者数も表示されます。

lib / main.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      // Add from here
      if (appState.attendees >= 2)
        Paragraph('${appState.attendees} people going')
      else if (appState.attendees == 1)
        const Paragraph('1 person going')
      else
        const Paragraph('No one going'),
      // To here.
      if (appState.loginState == ApplicationLoginState.loggedIn) ...[
        // Add from here
        YesNoSelection(
          state: appState.attending,
          onSelection: (attending) => appState.attending = attending,
        ),
        // To here.
        const Header('Discussion'),
        GuestBook(
          addMessage: (message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages,
        ),
      ],
    ],
  ),
),

ルールを追加する

すでにいくつかのルールが設定されているため、ボタンを使用して追加する新しいデータは拒否されます。あなたはに追加できるようにするルールを更新する必要がありますattendeesコレクション。

以下のためにattendeesドキュメント名として認証UIDを使用するので、コレクション、あなたはそれをつかむと、送信者のことを確認することができuid 、彼らが書いている文書と同じです。参加者リストは誰でも読むことができますが(プライベートデータがないため)、更新できるのは作成者だけです。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId;
    }
  }
}

検証ルールを追加する

データ検証を追加して、予想されるすべてのフィールドがドキュメントに存在することを確認します。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId
          && "attending" in request.resource.data;

    }
  }
}

(オプション)これで、ボタンをクリックした結果を表示することができます。 FirebaseコンソールでCloudFirestoreダッシュボードに移動します。

アプリのプレビュー

10.おめでとうございます!

Firebaseを使用して、インタラクティブなリアルタイムWebアプリケーションを構築しました。

私たちがカバーしたこと

  • Firebase認証
  • CloudFirestore
  • Firebaseのセキュリティルール

次のステップ

  • 他のFirebase製品についてもっと知りたいですか?ユーザーがアップロードした画像ファイルを保存したいですか?または、ユーザーに通知を送信しますか?チェックアウトFirebaseのマニュアルを。 Firebase用のFlutterプラグインについて詳しく知りたいですか?チェックアウトFlutterFireの詳細については。
  • Cloud Firestoreについてもっと知りたいですか?サブコレクションとトランザクションについて知りたいですか?頭の上のクラウドFirestoreのWebコードラボクラウドFirestoreの詳細深さに入るコードラボのために。それとも、このチェックアウトクラウドFirestoreを知るためにYouTubeのシリーズを

もっと詳しく知る

どうだった?

フィードバックをお待ちしております。 (非常に)短いフォームに必要事項を記入してくださいここに