search
LoginSignup
3

More than 1 year has passed since last update.

posted at

FlutterでFirestoreのデータをFutureBuilderを使って非同期で取得する

目的

今まではFirestoreから取得したデータを取得できたときにsetState()で再描画していたのですが、FutureBuilderを使えばStatelessWidgetにできて、簡単にきれいに書けることを知ったのでまとめてみます。
setState()で再描画するだけの処理にするのではなく、エラー分岐やデータ取得状況もわかるFutureBuilderを使ったほうがUXも高めれて良さそうなので積極的に使っていきたいですね。

環境

今回使っているFirestoreのバージョンは0.14.0です。
firebase_coreは0.5.0です。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^0.5.0
  cloud_firestore: ^0.14.0

非同期処理で使う関数

まずはFirestoreからデータを取得するFutureオブジェクトを作成します。
今回は指定のドキュメントIDのデータを取得するという簡単な関数にします。
コレクションIDとドキュメントIDを自身のものに置き換えてください。

Future<Map<String, dynamic>> getDocumentData() async {
  DocumentSnapshot snapshot = await FirebaseFirestore.instance
      .collection('コレクションのID')
      .doc('ドキュメントのID')
      .get();
  return snapshot.data();
}

FutureBuilderの使い方

Widgetのbuild()にFutureBuilderを追加します。
今回はScaffoldのbodyに直接置いていますが、実際はFirestoreのデータを表示したいところに追加してください。

FutureBuilderのfutureプロパティには作成したFutureの関数を指定します。
builderプロパティには(context, snapshot) { }を指定します。
一度、builderプロパティの関数が呼ばれます。
非同期処理が終わったら、再度builderが呼ばれて、エラー処理やデータが取得できなかったときに適切なWidgetを表示するようにしています。

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Flutter × Firestore'),
    ),
    body: FutureBuilder(
      future: getDocumentData(),
      builder: (context, snapshot) {
        // 取得が完了していないときに表示するWidget
        if (snapshot.connectionState != ConnectionState.done) {
          // インジケーターを回しておきます
          return const CircularProgressIndicator();
        }

        // エラー時に表示するWidget
        if (snapshot.hasError) {
          print(snapshot.error);
          return Text('エラー');
        }

        // データが取得できなかったときに表示するWidget
        if (!snapshot.hasData) {
          return Text('データがない');
        }

        // 取得したデータを表示するWidget
        return Text('${snapshot.data}');
      },
    ),
  );
}

取得したsnapshotの使い方

snapshotはAsyncSnapshot型なので、
FutureBuilderで取得したデータはsnapshot.dataでアクセス出来ます。

今回取得できるデータはFirestoreのデータなのでsnapshot.data['key']のようにFirestoreで指定したKeyを書けば取得できます。

builderプロパティでsnapshotに(context, AsyncSnapshot> snapshot) { }のように型を指定することも出来ます。

クラス全体のコード

class TestWidget extends StatelessWidget {
  Future<Map<String, dynamic>> getDocumentData() async {
    DocumentSnapshot snapshot = await FirebaseFirestore.instance
        .collection('コレクションのID')
        .doc('ドキュメントのID')
        .get();
    return snapshot.data();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter × Firebase'),
      ),
      body: FutureBuilder(
        future: getDocumentData(),
        builder: (context, snapshot) {
          // 取得が完了していないときに表示するWidget
          if (snapshot.connectionState != ConnectionState.done) {
            return const CircularProgressIndicator();
          }

          // エラー時に表示するWidget
          if (snapshot.hasError) {
            print(snapshot.error);
            return Text('エラー');
          }

          // データが取得できなかったときに表示するWidget
          if (!snapshot.hasData) {
            return Text('データがない');
          }

          // 取得したデータを表示するWidget
          return Text('${snapshot.data}');
        },
      ),
    );
  }
}

さいごに

Firestoreからデータを取得するたびにsetState()を呼ぶよりも、FutureBuilderを使ったほうがコードがきれいに書けて、取得状況もユーザーに知らせられるのでとても良いWidgetだと思います。
簡単に実装できるのでぜひ使ってみてください!
もし間違っている所があれば、ぜひ教えて下さい!

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
What you can do with signing up
3