2020/11/5 Get to know Firebase for Flutterの訳パート2

<<前のページへ

7. Read messages

Synchronize messages

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

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

lib / main.dartに、次のインポートを追加します。 これにより、StreamSubscriptionクラスにアクセスできるようになります。このクラスを使用して、ユーザーがログアウトしたときにクエリサブスクリプションをキャンセルできるようにします。

//lib.main.dart
import 'dart:async';  // new

次に、GuestBookウィジェットのすぐ上に次の値クラス(モデルクラス)があります。 このモデルクラスは、CloudFirestoreに保存しているメッセージデータの構造化ビューを公開します。

ウィジェットクラス内ではモデルクラスのインスタンスとしてメッセージデータを扱うのが定石。

モデルクラスを用意した方が、GuestBookMessageクラスに新しいフィールドを追加する必要が出た時などに、コードの修正が容易になる。

//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 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();

    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 = [];
          snapshot.docs.forEach((document) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'],
                message: document.data()['text'],
              ),
            );
          });
          notifyListeners();
        });
        // to here.
      } else {
        _loginState = ApplicationLoginState.loggedOut;
        // Add from here
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        // to here.
      }
      notifyListeners();
    });
  }

このセクションは重要です。この箇所でguesbookコレクションに対するクエリを定義して、サブスクライビングとアンサブスクライビング(listenの開始と終了)を管理するからです。工事中🏗

You listen to the stream, where you reconstruct a local cache of the messages in the guestbook collection, 

そしてサブスクリプションへの参照を変数にセットすることで、後のアンサブスクライブ(listenの終了処理)を可能にします。

ここでは多くのことが行われているので、デバッガーで時間をかけて、より明確なメンタルモデルを取得するときに何が起こるかを調べる価値があります。

詳細については、 CloudFirestoreのドキュメントを参照してください。

ヒント:更新を高速化するには、リスト全体ではなく、変更されたドキュメントのみを更新できます。詳細については、 CloudFirestoreのドキュメントをご覧ください。

GuestBookウィジェット内で、状態の変化をユーザーインターフェースと結びつける必要があります。GuesBookウィジェットにメッセージのリストを追加するために、GuestBookウィジェットを下記のように修正します。

新しいメッセージがguestbookコレクションに追加される度に、ApplicationState._guestBookMessagesにそれが反映される(guestbookコレクションをlistenしているため)ので、その変化をGuestBookウィジェットに反映させる。

//lib/main.dart
class GuestBook extends StatefulWidget {
  // Modify the following line
  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メソッドを下記のように修正して、GuestBookでメッセージのリストが表示されるようにします。

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;
                    },
                  ),
                ),
                SizedBox(width: 8),
                StyledButton(
                  onPressed: () async {
                    if (_formKey.currentState!.validate()) {
                      await widget.addMessage(_controller.text);
                      _controller.clear();
                    }
                  },
                  child: Row(
                    children: [
                      Icon(Icons.send),
                      SizedBox(width: 4),
                      Text('SEND'),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
        // Modify from here
        SizedBox(height: 8),
        for (var message in widget.messages)
          Paragraph('${message.name}: ${message.message}'),
        SizedBox(height: 8),
        // to here.
      ],
    );
  }
}

 

ビルドメソッドの前のコンテンツをColumnウィジェットでラップしてから、 Columnの子の末尾にコレクションを追加して、メッセージのリスト内の各メッセージの新しいParagraphを生成します。

最後に、新しいmessagesパラメータを使用してGuestBookを正しく構築するために、 HomePageの本文を更新する必要があります。

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

 


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

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

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


8.Set up basic security rules

 

 

 

 

 

参考

https://firebase.google.com/learn/codelabs/firebase-get-to-know-flutter#6

コメントを残す

メールアドレスが公開されることはありません。