25
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FlutterでFirestoreと連携したリアルタイム更新

Last updated at Posted at 2021-03-24

はじめに

Flutterを使ってネイティブアプリを作っているが、Widgetの使い方やFirestoreとのデータ連携の仕方など、コードの書き方が分からずそれなりに苦労している。
一旦分かってしまえばかなり高速でアプリ開発ができそうなので、使い方やコードの書き方のメモを残しておく。
今回はタイトルにある通り、Firestoreと連携したリアルタイム更新のやり方ついてメモを残す。
※Flutter2をインストールした開発環境では、一部動かない部分があったため、修正を加えた記事も投稿した。【FlutterでFirestoreと連携したリアルタイム更新(Mac M1)】

Flutterの実行環境

  • Ubuntu 18.04LTS(GCP上)
  • Flutter 1.22.6
  • Dart 2.10.5
  • Android Studio 4.1.2
  • VScode 1.53.0
      

メモ内容

事前準備として、FirestoreTestCollectionというコレクションを作成しておき、その中にtitleというフィールドを持つドキュメントを1つだけ作成しておく。
Qiita-No033_img01.jpg
※Firebaseへの接続が上手く行かない場合、こちらの記事を参考に。
 FlutterからCloud Firestoreのデータ取得 & データ書き込み

リアルタイム更新のやり方を書く前に、まずは、リアルタイムではなくボタンをクリックして、データを追加した後に画面を更新するやり方について。setState()を利用して状態を更新する様なプログラム。

main.dart
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

void main() {
 runApp(App());
}

class App extends StatefulWidget {
 @override
  _AppPage createState() => _AppPage();
}

class _AppPage extends State<App> {
  // ボタンクリックの挙動で使う変数
  var insert_data;
  String add_number;
  String add_title;

  // Firestoreからのデータを格納しておく変数
  List<DocumentSnapshot> fire_documents;

  // ボタンクリック時のアクション内容
  set_data(){

    add_number = (fire_documents.length + 1).toString();
    add_title = 'title' '$add_number';

    insert_data = {
      'title': add_title,
    };
    Firestore.instance.collection('TestCollection').add(insert_data);

    setState(() {});
  }

 @override
 Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(

        // Stateの更新時に、Widgetが構築される
        body: FutureBuilder<QuerySnapshot>(
          future: Firestore.instance.collection('TestCollection').getDocuments(),
          builder:(context, snapshot) {
            if (snapshot.hasData) {

              // List<DocumentSnapshot>`をsnapshotから取り出す。
              fire_documents = snapshot.data.documents;

              return ListView.builder(
                shrinkWrap: true,
                itemCount: fire_documents.length,   //配列の長さの分だけ作成する。
                itemBuilder: (context, index) {
                  return ListTile(
                    title:Text(fire_documents[index]["title"]),
                  );
                },
              );
            } else if (snapshot.hasError) {
              return Center(child:Text('snapshot Error'));
            }
          },
        ),

        // 追加ボタン
        floatingActionButton: Container(
          margin: EdgeInsets.only(bottom: 10.0), // ボタンの配置
          //width: 40.0, // ボタンのサイズ。形にも依るが先に記載されている大きさが優先っぽい。
          //height: 40.0,

          child: FloatingActionButton.extended(
            backgroundColor: Colors.blue,
            icon: Icon(Icons.add),
            label: Text("追加"),

            // テーマ追加ボタンクリック時の処理 ⇒ ダイアログ立ち上げる
            onPressed: () => set_data(),
          ),
        ),
    ),
  );
 }
}

上記のコードは、右下のFloatingボタンをクリックすると、Firestoreにデータが追加されてsetState()することで再描画される仕組みになっている。 ※細かな作り込みはしてないので悪しからず。。。

しかし、上記のコードだと別の誰かがFirestoreに書き込みをして中身が変わっていても、Stateが更新されない限りは反映されない。
そこで、リアルタイムに反映するために使えるのがStreamBuilder
StreamBuilderを使ったコードを書いてみると以下の様なコードになる。

main.dart
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

void main() {
 runApp(App());
}

class App extends StatefulWidget {
 @override
  _AppPage createState() => _AppPage();
}

class _AppPage extends State<App> {
 // ボタンクリックの挙動で使う変数
  var insert_data;
  String add_number;
  String add_title;

  // Firestoreからのデータを格納しておく変数
  List<DocumentSnapshot> fire_documents;

  // ボタンクリック時のアクション内容
  set_data(){
    add_number = (fire_documents.length + 1).toString();
    add_title = 'title' '$add_number';

    insert_data = {
      'title': add_title,
    };
    Firestore.instance.collection('TestCollection').add(insert_data);
  }

  @override
   Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(

        // リアルタイム更新  監視先は指定のコレクション全体
        body: StreamBuilder<QuerySnapshot>(
          stream: Firestore.instance.collection('TestCollection').snapshots(),
          builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {

            if (snapshot.hasError) {
              return Text('Something went wrong');
            }

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

            // user_id[array型]の中に自分のIDが含まれているドキュメントのみ取得されている
            fire_documents = snapshot.data.documents;

            // 中身のリスト表示部分
            return ListView.builder(
              // padding: const EdgeInsets.all(8),
              shrinkWrap: true,
              itemCount: fire_documents.length,   //配列の長さの分だけ作成する。
              itemBuilder: (context, index) {
                return ListTile(
                  title:Text(fire_documents[index]["title"]),
                );
              },
            );
        }),

        // 追加ボタン
        floatingActionButton: Container(
          margin: EdgeInsets.only(bottom: 10.0), // ボタンの配置
          //width: 40.0, // ボタンのサイズ。形にも依るが先に記載されている大きさが優先っぽい。
          //height: 40.0,

          child: FloatingActionButton.extended(
            backgroundColor: Colors.blue,
            icon: Icon(Icons.add),
            label: Text("追加"),

            // テーマ追加ボタンクリック時の処理 ⇒ ダイアログ立ち上げる
            onPressed: () => set_data(),
          ),
        ),
    ),);
  }
}

上記のコードは、画面上はあまり変化がない様に見えるが、試しにFirestoreにコンソールなどからデータを直接入力してみると、すぐに反映されるはず。

25
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?