FlutterFire CLI で Firebase と連携し Firestore からデータ取得

概要

公式ドキュメントにあるサンプルで、Firestore からデータを取得するサンプルを動かす。Firestore 上のデータは手動追加する(アプリから追加する場合)。

(cloud_firestore の公式 Example は複雑すぎた)。

環境

  • Flutter 2.10.2
  • flutterfire_cli 0.1.1+2
  • firebase_core 1.12.0
  • cloud_firestore: ^3.1.8
  • iPhone SE (2nd generation) シミュレータ

設計

Firestore データ取得

Firestore はドキュメントベース型の NoSQL 。

  • Collection の中に複数の Document が含まれる
  • Document には json 形式のデータを格納

取得する snapshot の種類

  • DocumentSnapshot
  • QuerySnapshot
  • QueryDocumentSnapshot

snapshot を取得するパターン

  • DocumentReference を指定し単一 Document を取得
  • Collection を指定し複数 Document を取得
    • Queryを使って複数のDocumentを取得するパターン」とも言えます
    • Queryを使うので返却されるsnapshotの型は QueryXxx となる

チュートリアル

Firebase CLI 準備

下記参照。

FlutterFire CLI 準備

Flutter プロジェクト作成

Flutter プロジェクト作成。

flutter create firestore_read --org com.runble1.firestoreRead
cd firestore_read

firebase_core を追加。

flutter pub add firebase_core
flutter pub add cloud_firestore

iOS 利用バージョンを上げておく。

vi Podfile
# platform :ios, '9.0'
platform :ios, '10.12'

ここで一度起動し、エラーが発生しないことを確認しておくといいぞ。
iOS バージョン上げたせいで起動遅いけど。

flutter run

FlutterFire CLI から Firebase プロジェクト作成

FlutterFire CLI から新規プロジェクトを作成する。

flutterfire configure

create a new project より、firestore-add プロジェクトを作成する。

  • Project 名 : firestore-read-sample
  • Platform : ios
i Found 3 Firebase projects.
✔ Select a Firebase project to configure your Flutter application with · <create a new project>
✔ Enter a project id for your new Firebase project (e.g. my-cool-project) · firestore-read-sample
i New Firebase project firestore-read-sample created succesfully.
✔ Which platforms should your configuration support (use arrow keys & space to select)? · ios
i Firebase ios app com.runble1.firestoreRead.firestoreRead is not registered on Firebase project firestore-read-sample.
i Registered a new Firebase ios app on Firebase project firestore-read-sample.

Firebase configuration file lib/firebase_options.dart generated successfully with the following Firebase apps:

Platform  Firebase App Id
ios       1:495423162590:ios:f883afab50eb61df499a4f

Learn more about using this file in the FlutterFire documentation:
 > https://firebase.flutter.dev/docs/cli

Firebase コンソールからもアプリが連携されたことを確認できる。
(ID は公開しないほうが良い、自分はすぐ消した)

lib/firebase_options.dart が作成される。
下記のように Firebase へのアクセス情報が記載されている(マスキングした)。

// File generated by FlutterFire CLI.
// ignore_for_file: lines_longer_than_80_chars
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
    show defaultTargetPlatform, kIsWeb, TargetPlatform;

/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
///   options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
  static FirebaseOptions get currentPlatform {
    if (kIsWeb) {
      throw UnsupportedError(
        'DefaultFirebaseOptions have not been configured for web - '
        'you can reconfigure this by running the FlutterFire CLI again.',
      );
    }
    // ignore: missing_enum_constant_in_switch
    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        throw UnsupportedError(
          'DefaultFirebaseOptions have not been configured for android - '
          'you can reconfigure this by running the FlutterFire CLI again.',
        );
      case TargetPlatform.iOS:
        return ios;
      case TargetPlatform.macOS:
        throw UnsupportedError(
          'DefaultFirebaseOptions have not been configured for macos - '
          'you can reconfigure this by running the FlutterFire CLI again.',
        );
    }

    throw UnsupportedError(
      'DefaultFirebaseOptions are not supported for this platform.',
    );
  }

  static const FirebaseOptions ios = FirebaseOptions(
    apiKey: 'aaaa',
    appId: '1:1111:bbbb',
    messagingSenderId: '2222',
    projectId: 'firestore-read-sample',
    storageBucket: 'firestore-read-sample.appspot.com',
    iosClientId: '3333-fnd0ta0494t9bhnk3i6sp823n4484rdi.apps.googleusercontent.com',
    iosBundleId: 'com.runble1.firestoreRead.firestoreRead',
  );
}

Firestore 設定

Firebase プロジェクトより左コンソールの Firestore Database を選択。

  • データベースを作成
  • テストモード(本番モードは次回以降)
  • asia-northeast1

users コレクションを作成する。ドキュメントフィールドは下記。

  • full_name
  • company

このデータをアプリで読み取る。

Firestore サンプル

公式サンプルを参考に、lib/main.dart を修正。

  • Firebase クラスで initializeApp メソッドを呼び出し
  • 生成されたオプションを initializeApp メソッドに提供
import 'package:flutter/material.dart';

import 'firebase_options.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Firestore Read Sample',
      home: UserListPage(),
    );
  }
}

class UserListPage extends StatelessWidget {
  final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance.collection('users').snapshots();
  //print(_usersStream);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ユーザ一覧'),
      ),
      body: Center(
        child: StreamBuilder<QuerySnapshot>(
          stream: _usersStream,
          builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
            //if (snapshot.hasError) {
            //  return Text('Something went wrong');
            //}
            if (snapshot.connectionState == ConnectionState.waiting) {
              return Text("Loading");
            }

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

起動・確認

起動。

flutter run

Firestore からデータが取得できていれば下記スクショのようになる。

まとめ

Firestore からデータを Read できた。

次回はテストモードで作成した Firestore を本番モードにしていく。

コードは Github にアップ。

GitHub - runble1/firestore_read
Contribute to runble1/firestore_read development by creating an account on GitHub.

エラー

FirebaseCommandException: An error occured on the Firebase CLI when attempting to run a command.

FlutterFire CLI から Firebase プロジェクトを作成する際に起きたエラー。

同じ名前(ID)の Firebase プロジェクトがすでに存在するとのこと。

自分の場合は、同じ名前で削除後に作成しよとして遭遇。

i New Firebase project flutter-firestore-runble1 created succesfully.
FirebaseCommandException: An error occured on the Firebase CLI when attempting to run a command.
COMMAND: firebase projects:create flutter-firestore-runble1 --json
ERROR: Failed to create project because there is already a project with ID flutter-firestore-runble1. Please try again with a unique project ID.

[!] The FlutterFire plugin cloud_firestore for macOS requires a macOS deployment target of 10.12 or later.

意図的に 10.12 以上を使うように指定する

vi ios/Podfile
platform :ios, '10.12'

FirebaseException ([core/not-initialized] Firebase has not been correctly initialized.

以下のようなエラーが発生。

Exception has occurred.
FirebaseException ([core/not-initialized] Firebase has not been correctly initialized.

Usually this means you've attempted to use a Firebase service before calling `Firebase.initializeApp`.

View the documentation for more information: https://firebase.flutter.dev/docs/overview#initialization
    )

DefaultFirebaseOptions の指定がない書き方をしていた。

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

Firebase.initializeApp で DefaultFirebaseOptions を指定するように変更。

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(MyApp());
}

_CastError (Null check operator used on a null value)

null を比較しようとしてエラー。データが取得できてない?

無理やりすすめるとシミュレータ側もエラーが出る。

該当のコード。 snapshot から data を取り出している部分。

children: snapshot.data!.docs.map((DocumentSnapshot document) {

つまりデータ取得ができていなかった。
自分の場合、Firestore を本番モードで作成していたため、セキュリティルールが全拒否となっていたことが原因。

暫定対応として、テストモードのセキュリティルールに上書きした。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if
          request.time < timestamp.date(2022, 3, 29);
    }
  }
}

じゃあ本番モードでのアクセスどうするの?

参考

Cloud Firestore | FlutterFire
To start using the Cloud Firestore package within your project, import it at the top of your project files:
FlutterFire CLIを利用してFlutter×Firebaseの環境構築をする - Qiita
Flutter 2.8のアップデートと共に、Flutter×Firebaseの更新が多数なされました。 その中でも、Flutter×Firebaseの環境構築を自動化してくれる、 FlutterFire CLIの追加は目を引くアップ...

コメント

タイトルとURLをコピーしました