Flutter×FirebaseでPJ始めるときにやること色々

FirebaseとFlutterを用いて開発することが多いのですが、毎回リリース直前になってFLAVORを分けたくなったり、セキュリティルールを検討し始めたりしてしまいます。
とくに複数人で開発していると、FLAVOR分けは他の人の開発の手を止めてしまう原因にもなってしまいますので、この辺の初期設定を脳死で出来るように手順化しておきます。
筆者は普段からWindowsとMacOSの両方で開発していますが、iOSのリリースも考えているなら、Macで全てやってしまうのがいいと思います。
今回使用したリポジトリは以下です。

https://github.com/tokku5552/flutter_firebase_sample

# この記事でやってること
- FirebaseプロジェクトをFlavor毎(Development/Production)に作成し、Flutterプロジェクトと紐づける
- セキュリティルールやCloud Functions、Hostingのための環境構築をDockerで行う
- LPやプライバシーポリシーをデプロイするためのFirebase Hostingの設定
- Lint(pedantic)の導入方法
- Flutter SDKのバージョン固定(fvmを使用)

# 筆者の環境

## Windows

```powershell
> flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 1.22.5, on Microsoft Windows [Version 10.0.19041.867], locale ja-JP)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.1)
[!] Android Studio (version 4.1.0)
    X Flutter plugin not installed; this adds Flutter specific functionality.
    X Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.54.3)
    X Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[!] Connected device
    ! No devices available

! Doctor found issues in 3 categories.
```

## MacOS

```bash
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 1.22.4, on macOS 11.2.3 20D91 darwin-x64, locale ja-JP)
 
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 12.4)
[!] Android Studio (version 4.1)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.53.0)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[!] Connected device
    ! No devices available

! Doctor found issues in 3 categories.
```

### 2021/09/17 Null Safety版の場合の手順を追記
Null Safety版の場合の手順を追記しました。
バージョンはそれぞれ以下のようになっています。

```json:fvm_config.json
 {
  "flutterSdkVersion": "2.5.0",
  "flavors": {}
}
```

```yaml:pubspec.yaml
  firebase_core: ^1.6.0
  firebase_analytics: ^8.3.2
  firebase_auth: ^3.1.1
  cloud_firestore: ^2.5.3
  firebase_storage: ^10.0.3
```

# 事前準備
以下のことを事前に行っている前提で進めます

- Firebaseのスキーマ設計
- Android StudioとXCodeのインストールおよびFlutterでの環境構築
- VSCodeのインストール(Flutter extensionsは必須ではない）
- Android StudioからFlutterプロジェクト作成
- Dockerが使える環境が整っている

※プロジェクト作成時にパッケージ名を決めるが、com.example　のドメインは予め避けておいたほうが良い。自分が取得しているドメイン名や、GitHubのドメイン名（io.github.<ユーザー名>)に変更しておく。
また、このパッケージ名は設定を行う際によく使用するので控えておく。Firebaseに登録する際に躓かないように、英字、数字、ピリオドのみを使用して決めておく方が良い（`_`アンダースコアと`-`ハイフンはそれぞれiOSなら`_`、Androidなら`-`が使用不可なので、OS毎に別のパッケージ名となってしまうことに注意する）

今回はDevelopmentという開発環境と、Productionという本番環境の２環境を用意することにする。

# Flavorごとのビルド設定
主に以下を参考に手順化しました。本記事では解説は行わないので、理由が知りたくなったらご参考ください。 
[環境設定 - KosukeSaigusa/recipe-app](https://github.com/KosukeSaigusa/recipe-app/tree/main/information/setup)
[Flutterで環境ごとにビルド設定を切り替える — iOS編 - medium](https://medium.com/flutter-jp/flavor-b952f2d05b5d)
## iOS側の設定
`open ios/Runner.xcworkspace`でXCodeからプロジェクトを開く
Runner>PROJECT>RunnerのDeployment Targetを10.0以上に変更しておく（現在のfirebase_coreのバージョンだと、ここが低いとビルドに失敗する）
![スクリーンショット 2021-03-21 16.59.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/2e6b43e1-2eec-6f21-c7b8-b251bef500ec.png)

XCodeのRunnerの部分をクリックし、Manage Schemes...をクリックする
![スクリーンショット 2021-03-21 17.14.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/aa8baa97-f4e5-f857-72e4-02dfe3ba56a4.png)

出てきた画面の左下の「＋」をクリックしてTarget,Nameが以下のようなものを追加する

| Target | Name |
|-----------|------------|
| Runner       | Development        |
| Runner     | Production      |

次にRunner>PROJECT>RunnerのConfigurationの下部の「＋」をクリックし、設定を追加する。
`Duplicate "Debug" Configuration`をクリックして`Debug-Development`を追加し、`Duplicate "Release" Configuration`をくりっくして`Release-Production`を追加する。
![スクリーンショット 2021-03-21 17.56.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/577a87bf-df92-bbac-a174-ebc8553e812b.png)


次に左ペインで右クリックしNew File...をクリックする。`Configuration Setting File`を選択し、NEXTをクリックする。

![スクリーンショット 2021-03-21 18.00.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/44947db8-345e-c587-7b98-a336c6a9bb38.png)

Configuration Setting Fileの内容は以下の通り。２回繰り返す。

| ファイル名 | 保存場所 | Group | Target|
|-----------|------------|---|---|
| Development.xcconfig| ios/Flutter|Flutter|Runnerにチェック|
| Production.xcconfig| ios/Flutter|Flutter|Runnerにチェック|
| Debug-Development.xcconfig| ios/Flutter|Flutter|Runnerにチェック|
| Release-Production.xcconfig| ios/Flutter|Flutter|Runnerにチェック|

![スクリーンショット 2021-03-21 18.06.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/12645532-db12-9e57-93f1-4feb32589846.png)

各ファイルを以下のように編集する

```configuration:Development.xcconfig
FLUTTER_FLAVOR=Development
PRODUCT_BUNDLE_IDENTIFIER=<パッケージ名>.development
DISPLAY_NAME=<開発環境のアプリ名>
FLUTTER_TARGET=lib/main.dart

```

```configuration:Production.xcconfig
FLUTTER_FLAVOR=Production
PRODUCT_BUNDLE_IDENTIFIER=<パッケージ名>
DISPLAY_NAME=<アプリ名>
FLUTTER_TARGET=lib/main.dart

```

```cpp:Release-Production.xcconfig
#include "Release.xcconfig"
#include "Production.xcconfig"

```

```cpp:Debug-Development.xcconfig
#include "Debug.xcconfig"
#include "Development.xcconfig"

```

Runner>PROJECT>RunnerのConfigurationsを以下のように設定します。

![スクリーンショット 2021-03-21 18.21.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/c3787c45-6eab-ae20-6272-3c0d9d21094c.png)

その後、Runner > PROJECT > Runner > Build Settings > Packaging の中の Product Bundle Identifierを以下のように、正しいパッケージ名を設定しておきます。
![スクリーンショット 2021-03-21 18.24.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/35f6f6c4-e3ef-a1a6-4791-c041e1b547ac.png)

Runner > TARGET > Runner も同様に設定しておく。

次にAndroid Studioからinfo.plistを開き最後の行（`</dict>と</plist>`)の手前に以下を追記する。

```ruby:info.plist
<key>FlutterFlavor</key>
<string>$(FLUTTER_FLAVOR)</string>
<key>CFBundleDisplayName</key>
<string>$(DISPLAY_NAME)</string>
```

XCodeに戻り、Runner>Runner>Info.plistをクリックし、`Bundle name`をリリースする際のアプリ名に変更する。（これはリリース前に行っても良い）

FirebaseのGoogleService-Info.plistを追加する前に先に設定を行う。
Runner>Runnerで右クリックし、New Groupをクリック　名前を`Firebase`としておく
![スクリーンショット 2021-03-21 18.48.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/1ede4ff3-118c-f05a-b24e-fb0e99389a72.png)

Runner > Targets > Runner > Build Phases を開き`New Run Script Phase`をクリックする
![スクリーンショット 2021-03-21 18.51.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/baa5aae6-4c51-2251-567e-35c3d1bd3372.png)

作成されたRun Scriptをダブルクリックし、適当な名前に変更しておく（例えば`Run Script for Firebase`）

以下をコピーしてスクリプト欄に貼り付ける。

```bash:
if [[ "${CONFIGURATION}" == "Debug-Development" ]]; then
    rm $PRODUCT_NAME/GoogleService-Info.plist
    cp $PRODUCT_NAME/Firebase/GoogleService-Info-Development.plist $PRODUCT_NAME/GoogleService-Info.plist
    echo "GoogleService-Info-Development.plist copied."
elif [[ "${CONFIGURATION}" == "Release-Production" ]]; then
    rm $PRODUCT_NAME/GoogleService-Info.plist
    cp $PRODUCT_NAME/Firebase/GoogleService-Info-Production.plist $PRODUCT_NAME/GoogleService-Info.plist
    echo "GoogleService-Info-Production.plist copied."
elif [[ "${CONFIGURATION}" == "Release" ]]; then
    rm $PRODUCT_NAME/GoogleService-Info.plist
    cp $PRODUCT_NAME/Firebase/GoogleService-Info-Production.plist $PRODUCT_NAME/GoogleService-Info.plist
    echo "GoogleService-Info-Production.plist copied."
else
    echo "configuration didn't match to Development, Staging or Production"
    echo $CONFIGURATION
    exit 1
fi
```

また、Output Fileに`$SRCROOT/Runner/GoogleService-Info.plist`を追加しておく。

![スクリーンショット 2021-03-21 18.55.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/dc912e0e-637b-00ae-089d-5b997e4ad158.png)

### 2021/10/31追記
- コメントからの情報
- 上記の手順で作成したRun Script for Firebaseの順序をCopy Bundle Resourcesの上に持ってこないとエラーが発生する場合があるようです。

```
Cycle inside Runner; building could produce unreliable results.
Cycle details:
→ Target 'Runner': CodeSign /Users/***/Library/Developer/Xcode/DerivedData/Runner-hcjkadykbvpqppdvadncqkaqgakj/Build/Products/Debug-iphoneos/Runner.app
○ That command depends on command in Target 'Runner': script phase “[CP] Embed Pods Frameworks”
○ That command depends on command in Target 'Runner': script phase “Thin Binary”
○ Target 'Runner' has copy command from '/Users/***/projects/***/ios/Runner/GoogleService-Info.plist' to '/Users/***/Library/Developer/Xcode/DerivedData/Runner-hcjkadykbvpqppdvadncqkaqgakj/Build/Products/Debug-iphoneos/Runner.app/GoogleService-Info.plist'
○ That command depends on command in Target 'Runner': script phase “Run Script for Firebase”
```

該当コメントは[こちら](https://qiita.com/tokkun5552/items/cd70c65dd06935d8c751#comment-1b73db993058cac662e8)

## Android側の設定

```gradle:android/app/build.gradle
   buildTypes {
        debug {
            debuggable true
            // 後の鍵の設定のために追加。現状はコメントアウトしておく
            // signingConfig signingConfigs.debug
        }
        release {
            // 後の鍵の設定のために追加。現状はコメントアウトしておく
            // signingConfig signingConfigs.release
        }
    }

    flavorDimensions "app"

    productFlavors {
        development {
            dimension "app"
            resValue "string", "app_name", "<開発環境のアプリ名>"
            applicationIdSuffix ".development"
        }
        production {
            dimension "app"
            resValue "string", "app_name", "<本番環境のアプリ名>"
        }
    }

```

## Android Studioでのbuild構成の編集
この項目は複数人での開発を行う場合には全員が行う必要がある項目です。
下図の「構成の編集」をクリックします
![スクリーンショット 2021-03-22 1.05.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/add9703d-5ee3-641b-5664-7600607588ac.png)


出てきたダイアログの左上にある「＋」をクリックし、新規構成の追加から`Flutter`を選択します。
![スクリーンショット 2021-03-22 1.06.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/33155fd5-0c2d-fdf6-6228-1a63519764b0.png)

すでに存在するmain.dartのDart entrypointをコピーして新規作成した名称未設定のDart Entrypointにペーストします。
![スクリーンショット 2021-03-22 1.07.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/adf1e8d7-92f5-f0f6-5fa5-cda7a55eaa87.png)
以下のように設定します。

|名前(Name)|追加引数(Additional Arguments)|Build flavor|
|-|-|-|
|Debug-Development|--debug --flavor development --dart-define=FLAVOR=development|development|
|Release-Production|--release --flavor production --dart-define=FLAVOR=production|production|

![スクリーンショット 2021-03-22 1.14.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/22f2f1b1-333f-e688-5fd9-7b12fbc8e3e1.png)

# Firebase Projectの作成
Firebase ProjectをFlavor毎に作成します。
プロジェクト名-dev、プロジェクト名-prod などと分けておくとわかりやすいです。
## iOSアプリの追加
下図のiOSをクリックします。
![スクリーンショット 2021-03-22 1.21.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/179a3607-48aa-6e64-5820-7349c9d3d275.png)
dev側はiOSのバンドルIDを`パッケージ名.development`、本番側はパッケージ名を入力し、アプリのニックネームはわかりやすい名前を入力しておきます。

![スクリーンショット 2021-03-22 1.30.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/92c8c49b-0ea7-eacd-d8ea-6e3431c5b3d8.png)

アプリを登録　をクリックすると、`GoogleService-Info.plist`をダウンロードできるので、`GoogleService-Info-Development.plist`にリネームし、XCodeから Runner>Runner>Firebaseへコピーする
![スクリーンショット 2021-03-22 1.36.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/f604fed7-6e96-6771-7493-b35dd87eba48.png)
本番環境もプロジェクトを作成し、同様にiOSアプリを追加し、`GoogleService-Info.plist`をダウンロードしたのちに、`GoogleService-Info-Production.plist`にリネームし、XCodeから Runner>Runner>Firebaseへコピーしておく。

この時点でAndroid StudioからiOSで`Debug-Development`でデバッグビルドできることを確認しておく。

### 2021/09/17追記
iOSでのビルドが失敗する場合は以下を試してみましょう。

- Podfileにplatform:ios, '10.0'を追加する
- iosフォルダで`pod install`を実行する
- `flutter clean` -> `flutter pub get`を実行する

```:error例1
could not find included file ‘Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig’ in search paths
```

```:error例2
Error output from CocoaPods:
↳
[!] Automatically assigning platform `iOS` with version `9.0` on target `Runner` because no platform was
specified. Please specify a platform for this target in your Podfile. See
`https://guides.cocoapods.org/syntax/podfile.html#platform`.

Error running pod install
```


https://halzoblog.com/flutter-ios-emulator/


以下のようなエラーの場合は`Podfile`に`IPHONEOS_DEPLOYMENT_TARGET`を書き換える処理を追記してあげます。

```:error
   1 error generated.
    note: Using new build system
    note: Building targets in parallel
    note: Planning build
    note: Analyzing workspace
    note: Constructing build description
    note: Build preparation complete
    /Users/<UserName>/StudioProjects/sansen/ios/Pods/Pods.xcodeproj: warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.5.99. (in target 'leveldb-library' from project 'Pods')
    /Users/<UserName>/StudioProjects/sansen/ios/Pods/Pods.xcodeproj: warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.5.99. (in target 'gRPC-Core' from project 'Pods')
    /Users/<UserName>/StudioProjects/sansen/ios/Pods/Pods.xcodeproj: warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.5.99. (in target 'gRPC-C++-gRPCCertificates-Cpp' from project 'Pods')
    /Users/<UserName>/StudioProjects/sansen/ios/Pods/Pods.xcodeproj: warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.5.99. (in target 'gRPC-C++' from project 'Pods')
    /Users/<UserName>/StudioProjects/sansen/ios/Pods/Pods.xcodeproj: warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.5.99. (in target 'abseil' from project 'Pods')
    /Users/<UserName>/StudioProjects/sansen/ios/Pods/Pods.xcodeproj: warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.5.99. (in target 'BoringSSL-GRPC' from project 'Pods')

Could not build the application for the simulator.
Error launching application on iPhone 12 Pro Max.
```

- Podfileの修正内容

もともと`post_install do |installer|`のブロックがある場合は内部に追加してください。(ブロックが２つあるとエラーになります。)

```ruby:ios/Podfile
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
    end
  end
end
```

- さらにXX/Flutter.h file not foundがいくつも出たので以下を実施

```
Xcode's output:
↳
    In file included from /Users/<UserName>/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.6.0/ios/Classes/FLTFirebasePluginRegistry.m:5:
    In file included from /Users/<UserName>/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.6.0/ios/Classes/FLTFirebasePluginRegistry.h:6:
    /Users/<UserName>/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.6.0/ios/Classes/FLTFirebasePlugin.h:15:9: fatal error: 'Flutter/Flutter.h' file not found
    #import <Flutter/Flutter.h>
            ^~~~~~~~~~~~~~~~~~~
    1 error generated.
    In file included from /Users/<UserName>/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.6.0/ios/Classes/FLTFirebasePlugin.m:5:
    /Users/<UserName>/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.6.0/ios/Classes/FLTFirebasePlugin.h:15:9: fatal error: 'Flutter/Flutter.h' file not found
    #import <Flutter/Flutter.h>
            ^~~~~~~~~~~~~~~~~~~
    1 error generated.
    note: Using new build system
    note: Building targets in parallel
    note: Planning build
    note: Analyzing workspace
    note: Constructing build description
    note: Build preparation complete
```

以下を実施する

```shell:
flutter pub cache repair
rm ios/Flutter/Flutter.podspec
rm -rf ~/Library/Caches/CocoaPods
rm -rf ios/Pods
flutter clean
flutter pub get
cd ios
pod install
```

https://www.yuukinishiyama.com/2021/01/16/flutter-fatal-error-flutter-flutter-h-file-not-found%E3%81%8C%E9%80%A3%E7%99%BA%E3%81%99%E3%82%8B/

https://ryuta46.com/1158

## Androidアプリの追加
同様にしてAndroidアプリも追加しておく。
Androidパッケージ名を、dev側は`パッケージ名.development`、本番側は単にパッケージ名を入力し、アプリのニックネームを適当に入力する。
![スクリーンショット 2021-03-22 1.52.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/0e52fea9-128f-9a19-2b82-4ffd8ac92ca9.png)

`アプリを追加`をクリックすると、`google-service.json`がダウンロードできるので、ダウンロードし、Android Studio から以下のようにフォルダを切って格納しておく。

![スクリーンショット 2021-03-22 1.58.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/c53da1b6-6eef-e5cf-3f76-6ae1c980ea3c.png)

またこのタイミングでAndroid側のFirebaseを使う設定と、flutterのパッケージの設定をしておきます。
Firebaseの指示に従って、以下を`android/build.gradle`に追加します。

```gradle:android/build.gradle
・・・
buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        google() // なければ追加する
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.3.4' // なければ追加する
    }
}

allprojects {
    repositories {
        google() // なければ追加する
        jcenter()
    }
}
・・・
```

加えて、`android/app/build.gradle`にも以下を追加します。

```gradle:android/app/build.gradle
・・・
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
apply plugin: 'com.google.gms.google-services' // 追加する
・・・
```

そして、Firestore/Authenticationを使う前提として、`pubspec.yaml`に以下を追加します。
※バージョンは[公式ページ](https://firebase.google.com/docs/flutter/setup?hl=ja)を参照してください。

```yaml:pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^0.4.5
  firebase_analytics: ^5.0.16
  firebase_auth: ^0.14.0+9
  cloud_firestore: ^0.12.11
```

### 2021/09/17追記
Null SafetyバージョンのFlutterを使用している場合は、公式ページのバージョンだとエラーとなるので、下記のFlutterFireを参考にする。

https://firebase.flutter.dev/docs/null-safety


追加したらpub getを実行してください。（私はいつも何故か２回実行しないとうまくいきません。）
下図のようにpubspec.yamlを開くと、右上に`Pub get`の文字があるのでそちらをクリックします。
![スクリーンショット 2021-03-22 2.11.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/da7bb216-c574-92f8-5e89-1585d8d03160.png)

## Firebaseのパッケージを追加したあと

現在のバージョンではflutterのfirebaseパッケージを組み込むと必ずエラーが発生しますので、以下の対処をしておきます。

- `minSdkVersion`を16から23に変更しておく
- `defaultConfig`に`multiDexEnabled true`の行を追加する。

参考：[アプリの圧縮、難読化、最適化 - Android Developpers (公式)
](https://developer.android.com/studio/build/shrink-code)

### 2021/09/17追記
下記のエラーが出るため、minSdkVersionは21ではなく23にしておく。

```
注意:/Users/<UserName>/development/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_admob-0.9.0+10/android/src/main/java/io/flutter/plugins/firebaseadmob/AdRequestBuilderFactory.javaは非推奨のAPIを使用またはオーバーライドしています。
注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。
注意:/Users/<UserName>/development/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-0.4.3+1/android/src/main/java/io/flutter/plugins/firebase/core/FirebaseCorePlugin.javaは非推奨のAPIを使用またはオーバーライドしています。
注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。
注意:/Users/<UserName>/development/flutter/.pub-cache/hosted/pub.dartlang.org/cloud_firestore-0.13.0+1/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.javaの操作は、未チェックまたは安全ではありません。
注意:詳細は、-Xlint:uncheckedオプションを指定して再コンパイルしてください。
注意:/Users/<UserName>/development/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_analytics-5.0.9/android/src/main/java/io/flutter/plugins/firebaseanalytics/FirebaseAnalyticsPlugin.javaは非推奨のAPIを使用またはオーバーライドしています。
注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。
注意:/Users/<UserName>/development/flutter/.pub-cache/hosted/pub.dartlang.org/package_info-0.4.0+13/android/src/main/java/io/flutter/plugins/packageinfo/PackageInfoPlugin.javaは非推奨のAPIを使用またはオーバーライドしています。
注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。
/Users/<UserName>/development/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.6/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java:25: 警告: [deprecation] FlutterPluginBindingのgetFlutterEngine()は非推奨になりました
    setupChannel(binding.getFlutterEngine().getDartExecutor(), binding.getApplicationContext());
                        ^                                               
警告1個                                                                 
注意:一部の入力ファイルは非推奨のAPIを使用またはオーバーライドしています。
注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。
```

参考：

https://qiita.com/Riscait/items/74c46f3deeb38e70f902

app/build.gradleはこちら

```gradle:app/build.gradle
    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "tech.tokkuengineer.flutter_firebase_sample"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        multiDexEnabled true
    }
```

- setting.gradleに以下を追加

参考：[【flutter】firebase登録時にsetting.gradeでのエラー - Qiita](https://qiita.com/yukou29good0910/items/c84b04d9a3fe1de8e7c8)

```gradle:android/setting.gradle
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()

def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}

plugins.each { name, path ->
    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
    include ":$name"
    project(":$name").projectDir = pluginDirectory
}
```

ここまで出来たら、エミュレータをAndroidのイメージに変えてDebug-Developmentでビルドできるか確認します。

# Dockerでのfirebase環境の構築
に入る前に、Firebaseプロジェクトの設定を行っておきます。
Firebase プロジェクトの画面から`Firestore`をクリック
![evidence_026.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/7f95dce7-6761-2ca6-3436-58ce89017cdc.jpeg)

Cloud Firestoreの画面から`データベースの作成`をクリック
![evidence_027.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/19e8f313-c54b-c253-d0d4-c379bf113544.jpeg)

データベースの作成画面でモードを選択
※本番環境モードで開始すると、セキュリティルールを適切に設定するまでFirestoreの書き込みが全て拒否されます。開発環境であれば、まずはテストモードで開始することをお勧めします。
![evidence_028.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/de77b198-0cf8-3db2-6670-f9971903e8f5.jpeg)

Cloud Firestoreのロケーションを選択する。好みではありますが、`asia-northeast1`を選択しておけば良いかと思います。
![evidence_029.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/eddfa70b-0c61-6f34-d794-7b2685b22192.jpeg)

## Dockerを使った環境構築
ここからはVSCodeで作業していきます。
`firebase/Dockerfile`

`Dockerfile`と`docker-compose.yaml`ファイルを作成し、以下のように記載します。
[GitHubリポジトリ](https://github.com/tokku5552/flutter_firebase_sample/tree/main/firebase)に用意してあります。

```dockerfile:firebase\firebase-cli\Dockerfile
FROM node:latest

# Define working directory
WORKDIR /workdir

# Firebase toolsのインストール
RUN apt-get -y update && apt-get install -y sudo openjdk-11-jdk
RUN npm install -g firebase-tools

# Expose ports
ENV HOST 0.0.0.0
EXPOSE 4000
EXPOSE 5000
EXPOSE 5001
EXPOSE 8080
EXPOSE 9005
```

```yaml:docker-compose.yaml
version: "3"
services:
  firebase-cli-container:
    build: ./firebase-cli
    container_name: firecli-container
    volumes:
      - ../firebase:/workdir
```

これらの準備が出来たら、`cd firebase`でフォルダを移動し、`docker-compose up`コマンドでコンテナをビルドします。

```powershell:
PS C:\Users\XXXX\AndroidStudioProjects\flutter_firebase_sample\firebase> docker-compose up
Building firebase-cli-container
failed to get console mode for stdout: The handle is invalid.
 => [internal] load build definition from Dockerfile                       0.0s
 => => transferring dockerfile: 32B                                        0.0s
 => [internal] load .dockerignore                                          0.0s
 => => transferring context: 2B                                            0.0s
 => [internal] load metadata for docker.io/library/node:latest             1.0s
 => [1/3] FROM docker.io/library/node:latest@sha256:096cbc2667f5d1507d59d  0.0s
 => CACHED [2/3] WORKDIR /workdir                                          0.0s
 => CACHED [3/3] RUN npm install -g firebase-tools                         0.0s
 => exporting to image                                                     0.0s
 => => exporting layers                                                    0.0s
 => => writing image sha256:9e31128f3f52b134c129a9f91f13f5bb91c3c9cd482f3  0.0s
 => => naming to docker.io/library/firebase_firebase-cli-container         0.0s
Successfully built a9f91f13fc9e311c31128f32fb1349c9cd482f3525bb9c15ecb15b874b35c88a
WARNING: Image for service firebase-cli-container was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating firecli ... done
Attaching to firecli
firecli exited with code 0
```

成功したら、以下のコマンドでコンテナを起動します。

```bash:
docker-compose run -p 4000:4000 -p 5000:5000 -p 5001:5001 -p 8080:8080 -p 9005:9005 firebase-cli-container /bin/bash
```

起動したら、コンテナ内に接続されているので、そのままfirebaseの初期設定をします。

まずはfirebaseにログインします。

```bash
> firebase login
i  Firebase optionally collects CLI usage and error reporting information to help improve our products. Data is collected in accordance with Google's privacy policy (https://policies.google.com/privacy) and is not used to identify you.

? Allow Firebase to collect CLI usage and error reporting information? (Y/n) 
```
そのままEnterを押すとURLが表示されるので、Ctrlを押しながらクリックして開きます。

アカウント選択の画面で、ログインするユーザをクリックします。
![evidence_001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/9d8bdf39-99da-7b72-c8bb-815da5280a08.jpeg)

Firebase CLIからのアカウントアクセスのリクエスト画面が出るので、許可をクリックします。
![evidence_002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/58002624-749a-b390-efb1-76e3065ebe0a.jpeg)

以下のような画面が出たら成功です。
![evidence_003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/16b564cf-1105-54a6-5700-3852497c769b.jpeg)

VSCodeに戻って（コンテナ内のコンソールに戻って）`firebase init`を実行します。

以下のような画面で`Firesotre`、`Functions`、`Hosting`、`Emulators`を選択します。
※選択するにはスペースを押します。
![evidence_030.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/5d51db7d-e1ac-2ea7-43ec-ea9d17bd65ec.jpeg)


後は基本的にデフォルトで進めます。
途中Firebaseプロジェクトを聞かれるので、先の手順で作成したプロジェクトのdev側を選択しておきます。
![evidence_015.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/944299f5-20a1-ada9-f931-71fd36e37705.jpeg)

`firebase init`が完了したら`firebase.json`を以下のように修正します。

```json:firebase.json
{
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "functions": {
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint",
      "npm --prefix \"$RESOURCE_DIR\" run build"
    ],
    "source": "functions"
  },
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  },
  "emulators": {
    "functions": {
      "host":"0.0.0.0",
      "port": 5001
    },
    "firestore": {
      "host":"0.0.0.0",
      "port": 8080
    },
    "hosting": {
      "host":"0.0.0.0",
      "port": 5000
    },
    "ui": {
      "enabled": true,
      "host":"0.0.0.0",
      "port":4000
    }
  }
}
```

ここまで出来たらエミュレータを起動してみます。
コンテナ上で以下のコマンドを実行します。

```bash
firebase emulators:start
```

ブラウザでhttp://localhost:4000 にアクセスし、以下のような画面が表示されたら成功です。

![evidence_025.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/37389646-758b-2d13-7a23-ac8fe6be032f.jpeg)

# Firebase Hostingの設定
Firebase Authenticationを使ってユーザー認証を行っていると、App Storeへの申請時にプライバシーポリシーが必要となります。
せっかくFirebaseを使っているので、プライバシーポリシーとついでにアプリのLPをFirebase Hostingで用意しましょう。

- プライバシーポリシー、利用規約作成
- ついでにLP作成

とはいえ、上記のDockerでの環境構築の手順で環境構築は済んでいます。
`firebase/public`がドキュメントルートになりますので、ここにLPなりプライバシーポリシーなりを作成して、デプロイすればOKです。
実際のコンテンツ作成はこの記事では割愛しますが、デプロイは以下のコマンドを用いて可能です。

```bash:
firebase deploy --only hosting
```

また別のFirebaseプロジェクトにデプロイする場合は、
`firebase projects:list`
でプロジェクトの一覧を表示したあと、表示されたProject IDを指定して以下のコマンドを実行するとプロジェクトを切り替える事ができます
`firebase use <Project ID>`

この後`firebase deploy`コマンドでデプロイすればプロジェクトが切り替わった状態でデプロイされます。

# その他の初期設定
## Lintの追加
これも後から入れるよりも最初に入れた方が良いので、[pedantic](https://pub.dev/packages/pedantic)を導入します。
`pubspec.yaml`に以下を追加します。

```yaml:pubspec.yaml
dev_dependencies:
  pedantic: ^1.11.0
```

[こちら（公式リポジトリ）](https://github.com/google/pedantic/blob/master/lib/analysis_options.yaml)にある最新の`analysis_options.yaml`をコピーしてプロジェクトの直下に置いておきます。
本記事執筆時点では1.11が最新でした。
[analysis_options.yaml](https://github.com/tokku5552/flutter_firebase_sample/blob/main/analysis_options.yaml)

もちろんお好みでカスタムてOKです。（私はいつもこの方法でコピーするだけにしてます。）

## Flutter SDKのバージョン固定
共同開発を行っていると、Flutter SDKのバージョンを固定したくなる場合があります。これもプロジェクトの初期の段階で導入を済ませてしまった方が良いです。
バージョン固定するために[fvm](https://pub.dev/packages/fvm)というパッケージを使います。
fvmを導入するために、FlutterにバンドルされているDartとは別にDartをインストールします。
[公式のインストール方法](https://dart.dev/get-dart)
### Windows

管理者権限でPowerShellを開き、Chocolatoryを以下のコマンドでインストールします

```powershell
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
```

インストールされたら`choco -v`でバージョンが表示されるか確認しておきましょう。

そのままdart-sdkをインストールします。

```powershell
choco install dart-sdk
choco upgrade dart-sdk
```

インストールと同時にPATHも通っていると思いますが念のため以下のコマンドで確認します。

```powershell
PS C:\WINDOWS\system32> dart --version
Dart SDK version: 2.10.4 (stable) (Wed Nov 11 13:35:58 2020 +0100) on "windows_x64"
```

インストール時のメッセージに従って一度PowerShellのプロンプトを再起動しておきます。
※またPub Cacheの方のPATHが正常に通らないので、手動で該当のフォルダを開いた後、パスを手でコピーしてインストール時に追加された環境変数のPATHを上書いておきましょう。

### MacOS

Terminalを開いて以下のコマンドを実行します

```
brew tap dart-lang/dart
brew install dart
```

また各shellに合わせてPATHを通しておきます。
インストール時のメッセージに従って`.zshrc`などに追記してください。
MacでのPATHの通し方が分からない方は[こちら](https://qiita.com/nbkn/items/01a11392921119fa0153)のような記事を参考にしてください。

## fvmの導入

WindowsならPowerShell、MacOSならTerminalを開いて以下のコマンドを実行します。

```bash
pub global activate fvm
```

次にプロジェクトのルートに行き、以下のコマンドを実行します
`stable`となっているところを好きなバージョンに変更してもかまいません

```
fvm install stable
fvm use <version>
```

このようにすると、Windowsなら`.fvm`フォルダの下に以下のようなファイルが出来上がります。

```json:fvm_config.json
{
  "flutterSdkVersion": "2.0.4"
}
```

これをgitの管理下にして共有しておくことで、他の共同開発者は、`fvm`をインストールした後にプロジェクトルートで

```
fvm install
```

とだけ打てば指定したバージョンがインストールされます。

MacOSの場合、`.fvm`の下に`fvm_config.json`に加えて`flutter_sdk`というシンボリックリンクが作成されます。
これをAndroid Studioの`Preferences -> Languages & Frameworks -> Flutter -> SDK`に指定すれば、`pub get`やビルドする際に、このflutter sdkを使うようになります。

**共同開発者にWindowsユーザーがいる場合はこのシンボリックリンクは`.gitignore`に追加して共有しないようにしておきます。（Windowsマシンでプロジェクトを開けなくなります）**

Windowsの場合は、シンボリックリンクが作成されないので、以下のような手順で手動でSDKを変更します。

1. `C:\Users\XXXX\fvm\versions`の下に、インストールされたバージョン名のフォルダがありますのでコピーしてAndroid Studioの`Preferences -> Languages & Frameworks -> Flutter -> SDK`に指定します。

2. Android Studioの`Preferences -> Languages & Frameworks -> Dart`を開き、`C:\Users\XXXX\fvm\versions\<指定したバージョン>\bin\cache\dart-sdk`を指定し「OK」を押します。

これで、WindowsでもfvmのFlutter SDKを見に行くようになります。

# まとめ
長々と手順化しましたが、これらはプロジェクトを作成して初期のタイミングで行った方が良いかと思います。
下手したらこれだけで1日潰れそうですね・・・

## 参考

- [Flutter アプリに Firebase を追加する(公式)](https://firebase.google.com/docs/flutter/setup?hl=ja)
- [環境設定 - KosukeSaigusa/recipe-app](https://github.com/KosukeSaigusa/recipe-app/tree/main/information/setup)
- [開発者ごとでなく、プロジェクトごとにFlutterのバージョンを管理する](https://laptrinhx.com/kai-fa-zhegotodenaku-purojekutogotoniflutternobajonwo-guan-lisuru-2244557157/)
- [fvmを使ってFlutter SDKのバージョンを切り替える - Qiita](https://qiita.com/Kurusu_ima/items/2dfd067f6e79f520198f)
- [[BUG] Can't load Kernel binary: Invalid kernel binary format version.](https://github.com/leoafarias/fvm/issues/100)
- [アプリの圧縮、難読化、最適化 - Android Developpers (公式)
](https://developer.android.com/studio/build/shrink-code)
- [docker でfirebaseを試したメモ - Qiita](https://qiita.com/hibohiboo/items/091286736c461fe68fd5)
- [Docker入門（第六回）〜Docker Compose〜](https://knowledge.sakura.ad.jp/16862/)
- [Docker compose ことはじめハンズオン - Qiita](https://qiita.com/TsutomuNakamura/items/7e90e5efb36601c5bc8a)
- [Firebase CLI の設定 - KosukeSaigusa/recipe-app](https://github.com/KosukeSaigusa/recipe-app/tree/main/firebase)
- [Flutterに関する記事まとめ | インフラエンジニアがもがくブログ](https://tokku-engineer.tech/flutter-all-article/)