FlutterでFirestoreからデータ取得するまで

# どんな記事

Firestore に登録されているデータを Flutter で取得するだけで色々な設定が必要だったのでまとめた。

なお、Firestore には
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539182/73638a40-4035-0474-e7af-9d2db5b45652.png)
のようなデータが登録されており、
ドキュメントID:3 の name(オムライス) をFlutterで取得して表示する。

# Firestore とは

https://cloud.google.com/firestore?hl=ja

> Cloud Firestore は、モバイルアプリやウェブアプリのデータの保存、同期、照会がグローバル スケールで簡単にできる NoSQL ドキュメント データベースです。

クラウドベースのNoSQLドキュメントデータベース。(詳細は上記ページにお任せ)
無料枠でどこまでできるか、等は下記を参照。

https://cloud.google.com/firestore/pricing?hl=ja

# 使用パッケージ等の事前準備

`pubspec.yaml` に以下を追加

```yaml
dependencies:
  firebase_core: ^2.13.1
  cloud_firestore: ^4.8.0
```

iOS、macOS　の場合、ビルドに5分以上かかるらしく、
`ios/Podfile` に
`pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '10.9.0'`
を追加する
([ここ](https://firebase.flutter.dev/docs/firestore/overview/#4-optional-improve-ios--macos-build-times) の内容)

例)
```
　：
target 'Runner' do
  use_frameworks!
  use_modular_headers!

  pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '10.9.0'
　：
```

(Firestoreに関する実装がない状態だとしても)実行してみて、

```
Error: CocoaPods's specs repository is too out-of-date to satisfy dependencies.
To update the CocoaPods specs, run:
  pod repo update

Error running pod install
```

のようなエラーが出たら、最後のバージョン指定(`:tag => '10.9.0'` の部分のバージョン番号) を変えてやるとうまいくかも。
(後から再現しようとすると `Error running pod install` だけの表示になってしまったので、その場合でも有効かも)

<details><summary>↑この部分について最初詰まったので詳細</summary><div>

Podfile への追記は、

https://firebase.flutter.dev/docs/firestore/overview/#4-optional-improve-ios--macos-build-times

> ４. (optional) Improve iOS & macOS build times#
Currently, the Firestore iOS SDK depends on some 500k lines of mostly C++ code which can take upwards of 5 minutes to build in Xcode. To reduce build times significantly, you can use a pre-compiled version by adding 1 line to your ios/Podfile inside your Flutter project;
> 
> pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '8.15.0'

の対応に当たる。

元々この記載通り、最後は `'8.15.0'` を指定して実行したが、

```
Error: CocoaPods's specs repository is too out-of-date to satisfy dependencies.
To update the CocoaPods specs, run:
  pod repo update

Error running pod install
```

のエラーが発生してしまった。

`pod repo update` を実施してみたが、状況変わらず、

`pod install` を試してみると

```
[!] CocoaPods could not find compatible versions for pod "FirebaseFirestore":
  In snapshot (Podfile.lock):
    FirebaseFirestore (= 10.9.0, ~> 10.9.0)

  In Podfile:
    FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `8.15.0`)

None of your spec sources contain a spec satisfying the dependencies: `FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `8.15.0`), FirebaseFirestore (= 10.9.0, ~> 10.9.0)`.

You have either:
 * out-of-date source repos which you can update with `pod repo update` or with `pod install --repo-update`.
 * mistyped the name or version.
 * not added the source repo that hosts the Podspec to your Podfile.
```

とエラーになっていることが分かった。
(警告文だったので(文字色が違うので)見逃していたが、最初の実行時のエラー文言の上にも同様の内容が記載されていた)

バージョンが合ってない、とのことなので、 `10.9.0` を指定すると無事に動いた。

https://qiita.com/kokogento/items/6e8122dda06d6b80a132
> Flutter 「CocoaPods could not find compatible versions for pod "FirebaseFirestore":」の解決法

にも感謝。

(おそらく影響ないので割愛するが、ここにたどり着くまでに `CocoaPods` を再インストールしたり、 `rbenv` でRubyのバージョンを上げたりもしたので、バージョン番号を変更するだけでは変わらないときはここらへんも試すとよいかも。)

</div></details>

# Firebase 利用プロジェクトとしての準備

https://firebase.flutter.dev/docs/overview/#initializing-flutterfire

らへんの流れ。

Firebaseの設定情報が格納された `firebase_options.dart` というファイルが必要なのだが、
このファイルを生成するのに `Firebase CLI` `FlutterFire CLI` が必要なのでそのインストールから。

`Firebase CLI` をインストールする

```shell
% curl -sL https://firebase.tools | bash
```

Firebase を使用しているGoogleアカウントと紐付ける

```shell
% firebase login
```

`FlutterFire CLI` をインストールする

```shell
% dart pub global activate flutterfire_cli
```

`~/.zshrc` にパスを追加する

```shell
export PATH="$PATH":"$HOME/.pub-cache/bin"
```

反映

```shell
% source ~/.zshrc
```

`flutterfire` の設定
(ここで「Firebase CLI」が必要)

```
% flutterfire configure
```

main.dart の main() を以下のようにする
(必ずしも main() である必要はないかも)

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

また、下記インポート文も追加する
(「firebase_options.dart」は「flutterfire configure」で作成される)

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

# Firestore 側の権限変更

Firestore側にアプリからのデータアクセスを許可する必要がある。
今回はテスト用に `読み込み権限のみ` 全開放する。

セキュリティルールについてはこちら

https://firebase.google.com/docs/rules?hl=ja

Firestore の ルール 画面にアクセスして、
読み込み書き込み、両方不可だったものを、
読み込みのみ許可へ。
:warning: ご自身の責任で許可してください。

```
rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      // allow read, write: if false;
      allow read: if true;
      allow write: if false;
    }
  }
}
```

権限不足のときは、アプリ実行時に
`FirebaseException ([cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.)`
のような例外が発生します。


# データを取得する

ここまで来ると無事に取得できると思います。

```dart
  Future<void> get() async {
    CollectionReference food = FirebaseFirestore.instance.collection('food');
    final doc = await food.doc('3').get();
    print(doc.get('name'));
  }
```