decochのブログ

フリーランスのiOSエンジニア decoch のブログです

Firestore × Flutterでリアルタイムにデータ変更を検知する

Flutter と Firebase を使ってリアルタイムのアプリを開発する方法を説明していきます。

firebase.flutter.dev

ライブラリの追加

pubspec.yaml を変更して追加します。

dependencies:
  flutter:
    sdk: flutter
  firebase_core: "^0.7.0"
  cloud_firestore: "^0.16.0"

ドキュメントの変更イベントを検知する

snapshots() を使うことで変更の検知できます。

1つのドキュメントの場合

class Example1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final example = FirebaseFirestore.instance.collection('examples').doc('id');

    return StreamBuilder<DocumentSnapshot>(
      stream: example.snapshots(),
      builder: (context, snapshot) {
        if (snapshot.hasError) {
          return const Text('Something went wrong');
        }

        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Text('Loading');
        }

        return Text(snapshot.data['title'] as String);
      },
    );
  }
}

複数ドキュメントの場合

class Example2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final examples = FirebaseFirestore.instance.collection('examples');

    return StreamBuilder<QuerySnapshot>(
      stream: examples.snapshots(),
      builder: (context, snapshot) {
        if (snapshot.hasError) {
          return const Text('Something went wrong');
        }

        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Text('Loading');
        }

        return ListView(
          children: snapshot.data.docs.map((document) {
            return ListTile(
              title: Text(document.data()['title'] as String),
              subtitle: Text(document.data()['subtitle'] as String),
            );
          }).toList(),
        );
      },
    );
  }
}

doc メソッドでドキュメントを指定するかどうかの違いだけで基本的には同じように書くことができます。

スナップショット間の変更の表示をする

新しいデータだけでなく現在表示している項目と比較したい場合、docChanges を利用すると実現することができます。

class Example3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final examples = FirebaseFirestore.instance.collection('examples');

    return StreamBuilder<QuerySnapshot>(
      stream: examples.snapshots(),
      builder: (context, snapshots) {
        if (snapshots.hasError) {
          return const Text('Something went wrong');
        }

        if (snapshots.connectionState == ConnectionState.waiting) {
          return const Text('Loading');
        }

        return ListView(
          children: snapshots.data.docChanges.map((change) {
            return ListTile(
              title: Text(change.doc.data()['title'] as String),
              subtitle: Text(change.doc.data()['subtitle'] as String),
            );
          }).toList(),
        );
      },
    );
  }
}

この場合、追加、変更、削除のいずれかの変更がFirestoreに行われています。 変更した種類に合わせて List のデータの更新処理を記述する必要があります。

/// An enumeration of document change types.
enum DocumentChangeType {
  /// Indicates a new document was added to the set of documents matching the
  /// query.
  added,

  /// Indicates a document within the query was modified.
  modified,

  /// Indicates a document within the query was removed (either deleted or no
  /// longer matches the query.
  removed,
}