Flutter에서 Firestore와 연계된 실시간 업데이트

소개



Flutter를 사용해 네이티브 앱을 만들고 있지만, Widget의 사용법이나 Firestore와의 데이터 연계 방법 등, 코드의 작성 방법을 모르고 그 나름대로 고생하고 있다.
일단 알고 버리면 상당히 고속으로 앱 개발을 할 수 있을 것 같기 때문에, 사용법이나 코드의 작성 방법의 메모를 남겨 둔다.
이번은 타이틀에 있는 대로,.
※Flutter2를 인스톨한 개발 환경에서는 일부 움직이지 않는 부분이 있었기 때문에, 수정을 더한 기사도 투고했다. 【Flutter로 Firestore와 연계한 실시간 업데이트(Mac M1)】

Flutter 실행 환경


  • 우분투 18.04LTS (GCP)
  • Flutter 1.22.6
  • Dart 2.10.5
  • Android Studio 4.1.2
  • VScode 1.53.0

  • 메모 내용



    사전 준비로서 Firestore에 TestCollection이라는 컬렉션을 작성해 두고, 그 안에 Firestore라는 필드를 가지는 문서를 1개만 작성해 둔다.

    ※Firebase에의 접속이 잘 가지 않는 경우, 이쪽의 기사를 참고에.
    Flutter에서 Cloud Firestore 데이터 가져오기 및 데이터 쓰기

    실시간 업데이트 방법을 작성하기 전에 먼저 실시간 대신 버튼을 클릭하고 데이터를 추가한 후 화면을 업데이트하는 방법에 대해. title를 사용하여 상태를 업데이트하는 프로그램.

    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() 하는 것으로 다시 그려지는 구조로 되어 있다. ※세세한 제작은 하고 있지 않기 때문에 나쁘지 않고. . .

    그러나, 위의 코드라면 다른 누군가가 setState() 에 기입을 해 내용이 바뀌어도, State가 갱신되지 않는 한은 반영되지 않는다.
    거기서, 리얼타임에 반영하기 위해서 사용할 수 있는 것이 FirestoreStreamBuilder 를 사용한 코드를 작성해 보면 다음과 같은 코드가 된다.

    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(),
              ),
            ),
        ),);
      }
    }
    

    위의 코드는 화면에 별다른 변화가 없는 것처럼 보이지만, 시험에 StreamBuilder 에 콘솔 등에서 데이터를 직접 입력해 보면 곧바로 반영될 것이다.

    좋은 웹페이지 즐겨찾기