FlutterをFlavorで複数環境に分ける備忘録

前提

Flutter 3.27.2 既存のFlutterプロジェクトを複数環境に分ける Flavorを使って環境をdev, stg, prodに分ける Firebaseの設定も行う 実行はVSCodeを使うので、VSCodeの設定もする

初期化

android/ ios/ などのプラットフォームのディレクトリを全て消す。

flutter create .

を実行してプラットフォームのファイルを作り直す。

Androidの設定

まずはAndroidの設定。iOSはめんどくさいのでひとまず無視。 Firebaseの設定もひとまずしない。 android/app/build.gradleにFlavorの設定を追加する。

android {
    // ...
    flavorDimensions "default"
    productFlavors {
        dev {
            dimension "default"
            resValue "string", "app_name", "App Dev"
            applicationIdSuffix ".dev"
        }

        stg {
            dimension "default"
            resValue "string", "app_name", "App Stg"
            applicationIdSuffix ".stg"
        }

        prod {
            dimension "default"
            resValue "string", "app_name", "App Prod"
        }
    }
}

build.gradle.ktsの場合は

  android {
    ...
    flavorDimensions("default")
    productFlavors {
        create("dev") {
            setDimension("default")
            applicationIdSuffix = ".dev"
            resValue("string", "app_name", "App Dev")
        }

        create("stg") {
            setDimension("default")
            applicationIdSuffix = ".stg"
            resValue("string", "app_name", "App Stg")
        }

        create("prod") {
            setDimension("default")
            resValue("string", "app_name", "App Prod")
        }
    }
  }

しっかりFlavorが切り替わったことを確認するために、android/app/src/main/AndroidManifest.xmlを編集して、app_nameをFlavorによって変える処理を入れておく。

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
        android:label="@string/app_name"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">

VSCodeの設定

VSCodeの.vscode/launch.jsonを編集して、dev, stg, prodでの起動ができるようにしておく。 これで実行できるようにする。 モノレポなので、cwdを設定してるが、モノレポじゃなければ不要。

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "app(dev)(debug)",
      "cwd": "packages/flutter_app",
      "request": "launch",
      "type": "dart",
      "flutterMode": "debug",
      "args": [
        "--flavor",
        "dev"
      ]
    },
    {
      "name": "app(stg)(debug)",
      "cwd": "packages/flutter_app",
      "request": "launch",
      "type": "dart",
      "flutterMode": "debug",
      "args": [
        "--flavor",
        "stg"
      ]
    },
    {
      "name": "app(prod)(debug)",
      "cwd": "packages/flutter_app",
      "request": "launch",
      "type": "dart",
      "flutterMode": "debug",
      "args": [
        "--flavor",
        "prod"
      ]
    },
  ]
}

Androidエミュレーターで実行してみると次のエラーが出たので、Androidの設定を修正。

┌─ Flutter Fix ─────────────────────────────────────────────────────────────────────────────────┐ │ The plugin firebase_auth requires a higher Android SDK version. │ │ Fix this issue by adding the following to the file │ │ /Users/fukui/Package/firebird/packages/firebird_app/android/app/build.gradle: │ │ android { │ │ defaultConfig { │ │ minSdkVersion 23 │ │ } │ │ } │ │ │ │ Following this change, your app will not be available to users running Android SDKs below 23. │ │ Consider searching for a version of this plugin that supports these lower versions of the │ │ Android SDK instead. │ │ For more information, see: https://flutter.dev/to/review-gradle-config │ └───────────────────────────────────────────────────────────────────────────────────────────────┘

android/app/build.gradleのminSdkVersionを23に変更する。 元々はminSdk = flutter.minSdkVersionだった。

 android {            
    // ..                                                                         │
│   defaultConfig {        
      // ..                                                                     │
│     minSdkVersion = 23                                                                          │
│   }                                                                                           │
│ } 

build.gradle.kts

defaultConfig {
    ...
    minSdkVersion(23)           
    ...
}

再度エミュレーターで実行してみる。 dev, stg, prodでそれぞれ起動してみてアプリ名が変わっていれば、Flavorの設定ができている。

iOSの設定

iOSの設定は少しだるいが、手順通りやれば大丈夫。 プロジェクトのルートで次のコマンドを実行して、XCodeを開く

open ios/Runner.xcworkspace

左上のメニューからProduct > Scheme > New Schemeを選択する。 ダイアログが出てきて名前の入力を促されるので、devと入力してdevスキーマを作成する。 この名前がflavorに対応する。

次に、画面中央あたりにあるProjectのRunnerを選択。Infoというタブを探す。 Configurationsという項目の+ボタンを押す。 Duplicated "Debug" Configurationのような選択肢が出てくるので、Debug, Release, Profileを全てコピーする。

それぞれに名前をつけれるので、Debug-dev, Release-dev, Profile-devのようにしておく。

最後にメニューからProduct > Scheme > Manage Schemes...を選択し、先ほど作ったdevを選択する。 Run, Test, Profile, Analyze, ArchiveのBuild ConfigurationをDebugからDebug-devに変更する。

ProfileなどはProfile-devと設定としておく。

次にTARGETSのRunnerからBuild Settingのタブを探す。 All, Combinedを選択して、検索でproduct bundleと検索、Product Bundle Identifierの設定を変更する。 Debug-devの設定をcom.example.flutter-template.devとする。

またProduct Nameと検索してDebug-devの設定をApp Devにする。

Info.plistのBundle Display Nameの項目を$(PRODUCT_NAME)にする。

PodfileにDebug-devなどを追加する。 ついでにpodfileのiosバージョン設定を13にしておく platform :ios, '13.0'

platform :ios, '13.0'

project 'Runner', {
  'Debug' => :debug,
  'Profile' => :release,
  'Release' => :release,
  'Debug-dev' => :debug,
  'Profile-dev' => :release,
  'Release-dev' => :release,
}

同様に、Stg, Prodスキーマも作成して設定する。

Firebaseの設定

flutterfireを利用して設定をおこなう。 設定するためのシェルスクリプトを作成しておく。これはプロジェクトのルートに配置。

#!/bin/bash
# Script to generate Firebase configuration files for different environments/flavors
# Feel free to reuse and adapt this script for your own projects

if [[ $# -eq 0 ]]; then
  echo "Error: No environment specified. Use 'dev', 'stg', or 'prod'."
  exit 1
fi

case $1 in
  dev)
    flutterfire config \
      --project=flutter-template-dev \
      --out=lib/firebase_options_dev.dart \
      --ios-bundle-id=com.example.flutter-template.dev \
      --ios-out=ios/flavors/dev/GoogleService-Info.plist \
      --android-package-name=com.example.flutter_template.dev \
      --android-out=android/app/src/dev/google-services.json
    ;;
  stg)
    flutterfire config \
      --project=flutter-template-stg \
      --out=lib/firebase_options_stg.dart \
      --ios-bundle-id=com.example.flutter-template.stg \
      --ios-out=ios/flavors/stg/GoogleService-Info.plist \
      --android-package-name=com.example.flutter_template.stg \
      --android-out=android/app/src/stg/google-services.json
    ;;
  prod)
    flutterfire config \
      --project=flutter-template-prod \
      --out=lib/firebase_options_prod.dart \
      --ios-bundle-id=com.example.flutter-template \
      --ios-out=ios/flavors/prod/GoogleService-Info.plist \
      --android-package-name=com.example.flutter_template \
      --android-out=android/app/src/prod/google-services.json
    ;;
  *)
    echo "Error: Invalid environment specified. Use 'dev', 'stg', or 'prod'."
    exit 1
    ;;
esac

権限がないので付与。

chmod +x packages/firebird_app/flutterfire-config.sh

実行

./flutterfire-config.sh stg 

✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration ✔ Please choose one of the following build configurations · Debug-stg

のように答えると、設定ファイルが追加される。

最後にDartでFlavorによって、Firebaesの初期化を分岐させる。

import 'package:template_app/firebase_options_dev.dart' as dev;
import 'package:template_app/firebase_options_prod.dart' as prod;
import 'package:template_app/firebase_options_stg.dart' as stg;

await Firebase.initializeApp(
    options: switch (appFlavor) {
      'dev' => dev.DefaultFirebaseOptions.currentPlatform,
      'stg' => stg.DefaultFirebaseOptions.currentPlatform,
      'prod' => prod.DefaultFirebaseOptions.currentPlatform,
      _ => throw Exception('Invalid app flavor: $appFlavor'),
    },
  );