Udemyセール開催中! 対象コースが1,220円から

【FlutterFire】Firestoreのデータをリアルタイムで取得する

今回はFirebaseFirestoreを使用してCloud Firestoreで保存されているデータを取得した後、「StreamBuilder」「ListView」「ListTile」を使って表示させる方法を紹介します。

目次

下準備

  1. Firebaseで新規プロジェクト作成
  2. Cloud Firestoreで取得するコレクション・ドキュメントを作成
  3. pub.devから「firebase_core」と「firebase_firestore」を導入

Cloud Firestoreから取得するデータ

今回はCloud Firestoreのコレクション「messages」に5つのドキュメント「text: 'Hello World'」が存在している程で解説していきます。

Cloud Firestoreのデータをリアルタイムで取得

記事下の完成コードで示している「ステップ○」を参考にして読んでみて下さい。

ステップ1:FirebaseFirestoreをインスタンス化する

//ステップ1
final _firestore = FirebaseFirestore.instance;

FirebaseFirestoreをインスタンス化します。

ステップ2:StreamBuilderでFirestoreのデータを表示させる

//ステップ2
child: StreamBuilder(
  stream: _firestore.collection('messages').snapshots(),
  builder:
      (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
    //データを取得できなかった場合の返り値を指定
    if (snapshot.hasError) {
      return Center(
        child: Text('取得できませんでした'),
      );
    }
    //取得中の返り値を指定
    if (!snapshot.hasData) {
      return Center(
        child: Text("Loading"),
      );
    }
    //データを取得できた場合の返り値を指定
    return ListView(
      children: snapshot.data!.docs.map((DocumentSnapshot document) {
        Map<String, dynamic> message =
            document.data()! as Map<String, dynamic>;
        return Card(
          child: ListTile(
            title: Text(message['text']),
          ),
        );
      }).toList(),
    );
  },
),

少しコードが長いですが内容はシンプルです。

それではStreamBuilderのstreamプロパティとbuilderプロパティに分けて解説します。

stream

stream: _firestore.collection('messages').snapshots(),

StreamBuilderのstreamプロパティに、Firestoreから取得したいコレクションをStream型で渡す必要があります。よって「.collection()」のメソッド「.snapshots()」を使用してStreamデータを渡します。

builder

builder:
    (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
  if (snapshot.hasError) {
    return データを取得できなかった時の返り値;
  }

  if (!snapshot.hasData) {
    return データ取得中の返り値;
  }

  //データを取得できた場合ListViewを返す
  return ListView(
    children: snapshot.data!.docs.map((DocumentSnapshot document) {
      Map<String, dynamic> message =
          document.data()! as Map<String, dynamic>;
      return Card(
        child: ListTile(
          title: Text(message['text']),
        ),
      );
    }).toList(),
  );
},

StreamBuilderのbuilderプロパティは上記のようにコードを書きます。builderプロパティでは必ずWidgetを返す必要があります。

コードを見ていると、オブジェクト「snapshot(AsyncSnapshot)」の使い方が分かりにくいと思いますのでドキュメントを見ながら記事を読むのがおすすめです。

builderプロパティではデータを取得できた場合の返り値(Widget)以外にも、if文を使ってstreamのステート別に返り値を指定しておきましょう。(下の条件分岐を参照)

  • snapshot.hasError:エラーが発生した返り値
  • !snapshot.hasData:取得中の返り値(データが無い時)

取得したデータをListViewで表示させる

return ListView(
  children: snapshot.data!.docs.map((DocumentSnapshot document) {
    Map<String, dynamic> message =
        document.data()! as Map<String, dynamic>;
    return Card(
      child: ListTile(
        title: Text(message['text']),
      ),
    );
  }).toList(),
);

それでは取得したデータをListViewListTileを使って表示させていきます。

ListViewのchildrenプロパティにはList型(<Widget>[])を渡す必要があります。よってsnapshotで取得したドキュメント別にデータを取得して、ListTileを作成し、Listに格納する必要があります。

  • data:snapshotの最新データを取得
  • docs:snapshotに含まれている全てのドキュメントをList(iterable)で取得
  • map():itrebleから取得したデータを任意のデータに変更
  • toList(): IterableをList化 => childrenプロパティに返す

今回は「docs」で全てのドキュメントをListとして取得した後、「map()」で各ドキュメントごとにデータを取得してListTileに変換し、「toList()」で作成したListTileをリスト化しています。

サンプルコード

少し複雑なので理解できなかった方は、ドキュメントを参考にしながら再度記事を読み返してみるのがおすすめです。

//ソース
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  //ステップ1
  final _firestore = FirebaseFirestore.instance;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Chat')),
        body: SafeArea(
          //ステップ2
          child: StreamBuilder(
            stream: _firestore.collection('messages').snapshots(),
            builder:
                (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
              if (snapshot.hasError) {
                return Center(
                  child: Text('取得できませんでした'),
                );
              }

              if (!snapshot.hasData) {
                return Center(
                  child: Text("Loading"),
                );
              }

              return ListView(
                children: snapshot.data!.docs.map((DocumentSnapshot document) {
                  Map<String, dynamic> message =
                      document.data()! as Map<String, dynamic>;
                  return Card(
                    child: ListTile(
                      title: Text(document['text']),
                    ),
                  );
                }).toList(),
              );
            },
          ),
        ),
      ),
    );
  }
}

参考

  • URLをコピーしました!
目次
閉じる