🔨

【Flutter 3.19対応】Dart-define-from-fileを使って開発環境と本番環境を分ける

2021/08/23に公開
40
開発環境 ステージング環境 本番環境

はじめに

こんにちは、Flutterでのアプリ開発をメインとしている「Altive株式会社」の村松龍之介(@riscait)です!

Flutterにおいて、その環境分けの方法はいくつかありますが、今回は Dart-defines-from-file を使用して実現する方法をご紹介します。

dart-define-from-file のメリット

Flavor用のパッケージを使う場合等と比べて、ビルドコマンドがシンプルになることや、自動生成ファイルが少なく、取り回しがしやすいことが大きな利点です。

  • main.dart を環境ごとに分ける必要がない
  • iOSの schemeConfiguration を作成する必要がない
  • パッケージの導入が不要(アイコン生成には使用します)

前提

  • この記事では以下の3環境に分けていきます
    (あくまで一例なので、数や名前は適宜変更してください)
    • dev: ローカルの開発環境
    • stg: 本番環境に似せた環境を用意し、検証を行う環境
    • prod: 実際に公開するアプリで使用する本番環境
  • Flutter 3.7 以上を想定
    • 当記事の手法はFlutter 3.7 以上で利用可能です。
  • 対応OS
    • ✅ Android
    • ✅ iOS
    • ✅ macOS
    • ✅ Web

サンプルコード(リポジトリ)

  • 環境分けを適用したアプリプロジェクトのサンプルはこちら↓

https://github.com/altive/flutter_app_template/tree/main/packages/flutter_app

当記事で例として使用する環境分け情報

  • アプリ名
    • dev: FAT dev
    • stg: FAT stg
    • prod: FAT
  • アプリID (Bundle ID, Package name)
    • dev: jp.co.altive.fat.dev
    • stg: jp.co.altive.fat.stg
    • prod: jp.co.altive.fat

dev, stg環境の場合はアプリ名とアプリIDに、それぞれ環境名を足したもので表現したいと思います。
もちろん、「アプリ名の方は(dev)のように括弧で表現する」といったことも自由なので、適宜ご調整ください。

Firebaseを使用する場合

  • Firebaseを使用している場合は、 flutterfire-cli を使用したり、FirebaseのWebコンソールから環境数分のプロジェクトとアプリを作成して、iOS用の GoogleService-Info.plistgoogle-services.json をダウンロードしておきましょう。

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

dart-define-from-fileを使う

それでは、 dart-define-from-file を使った環境分けを行っていきましょう。

環境の数だけ定義をまとめたファイルを作成する(JSON or ENV)

まずは環境名や、環境ごとのアプリ名など、必要な項目を定義するファイルを作成しましょう👌

ファイルは、 .json.env が使えます。
ファイルの配置場所とファイル名は自由です。

今回は例として dart_defines ディレクトリを作成して dev.env という名前で作成するとします。

dart_defines/dev.env
flavor="dev"
appName="FAT dev"
appId="jp.co.altive.fat.dev"
googleReversedClientId="com.googleusercontent.apps.0123456789-xxxxxxxxxxxxxxxx"

同じように環境数分のファイルを作成しましょう。

dart_defines/stg.env
flavor="stg"
appName="FAT stg"
appId="jp.co.altive.fat.stg"
googleReversedClientId="com.googleusercontent.apps.0123456789-xxxxxxxxxxxxxxxx"
dart_defines/prod.env
flavor="prod"
appName="FAT"
appId="jp.co.altive.fat"
googleReversedClientId="com.googleusercontent.apps.0123456789-xxxxxxxxxxxxxxxx"

アプリビルド時にコマンドで環境を指定する

アプリ起動(run)やビルド(build)時に環境を分けるために、 --dart-define-from-file というオプションを指定します。
下記の例では、 dev (開発環境)を指定しています。

# アプリ起動コマンドの例
flutter run --dart-define-from-file=dart_defines/dev.env
# アプリビルドコマンドの例
flutter build ios --dart-define-from-file=dart_defines/dev.env

VS Code の設定例

VS Code では、 launch.json で起動コマンドを編集できます。
すでにファイルがある場合は、既存ファイルを編集し、ない場合は .vscode ディレクトリを作成し、 launch.json を追加しましょう。

以下のように args にて --dart-define-from-file を指定可能です。
dev, stg, prod 3環境分用意した例です。

launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug dev",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--dart-define-from-file=dart_defines/dev.env"
            ]
        },
        {
            "name": "Debug stg",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--dart-define-from-file=dart_defines/stg.env"
            ]
        },
        {
            "name": "Debug prod",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--dart-define-from-file=dart_defines/prod.env"
            ]
        }
    ]
}

Android Studio の設定例

Add Configuration または Edit Configurations からFlutterの起動構成を設定しましょう。

詳しくは、Daigoさんが書いてくださった記事を参考にしてください👍
https://zenn.dev/mamushi/scraps/13c0232c88227e

FlutterアプリでDart defineで設定した情報を取得する

起動/ビルドコマンドで指定した dart-define-from-file がきちんと反映されているかも確認することができるので試しておきましょう。

env(またはjson)ファイル内で定義したプロパティは fromEnvironment メソッドで個別に取得することが可能です👍

  • 文字列: String.fromEnvironment(name)
  • 数値: int.fromEnvironment(name)
  • 真偽値: bool.fromEnvironment(name)
// `--dart-define-from-file=dart_defines/dev.env` に定義した `flavor` プロパティを取得したい場合
const flavor = String.fromEnvironment('flavor');
print(flavor) // 'dev'

アプリIconを環境別に分ける

※ Icon画像を環境別に分けない場合は読み飛ばしてください。

環境別のアイコン画像をフォルダに配置します

※ 画像は適宜用意してください
assets/launcher_icon/icon-dev.png
assets/launcher_icon/icon-stg.png
assets/launcher_icon/icon-prod.png

flutter_launcher_iconsパッケージをインストール

flutter pub add flutter_launcher_icons --dev

もしくは pubspec.yaml に追記して pub get します。

pubspec.yaml
dev_dependencies:
  flutter_launcher_icons: ^0.13.1 # インストール時点での最新版を推奨

Flavorごとの設定ファイルを作成

  • flutter_launcher_icons-dev.yaml
  • flutter_launcher_icons-stg.yaml
  • flutter_launcher_icons-prod.yaml
flutter_launcher_icons-dev.yaml
flutter_launcher_icons:
  android: true
  ios: true
  image_path: "assets/launcher_icon/icon-dev.png"

アイコン画像の書き出し実行

dart run flutter_launcher_icons

✓ Successfully generated launcher icons for flavors と表示されれば成功です。

iOSでは ios/Runner/Assets.xcassets/AppIcon-{flavor}.appiconset/
Androidでは android/app/src/{flavor}/mipmap**/ic_launcher.png

に出力されているはずです👍

Androidアプリに必要な対応

定義した Dart-define を反映させるために build.gradleAndroidManifest.xml ファイルを編集していきましょう。

build.gradle を編集して dart-define を受け取る

Dart-define をデコードして受け取ります。

android/app/build.gradle
// dart-define を入れる変数を宣言しています。
def dartDefines = [:];
if (project.hasProperty('dart-defines')) {
    // カンマ区切りかつBase64でエンコードされている dart-defines をデコードして変数に格納します。
    dartDefines = dartDefines + project.property('dart-defines')
        .split(',')
        .collectEntries { entry ->
            def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
            pair.length == 2 ? [(pair.first()): pair.last()] : [:]
        }
}

defaultConfig でアプリIDとアプリ名を設定

android/app/build.gradle
 defaultConfig {
     minSdkVersion flutter.minSdkVersion
     targetSdkVersion flutter.targetSdkVersion
     versionCode flutterVersionCode.toInteger()
     versionName flutterVersionName
+     applicationId "${dartDefines.appId}"
+     resValue "string", "app_name", "${dartDefines.appName}"
 }
  • applicationId でアプリ名を指定
  • resValue を使用してアプリ名を指定

defaultConfigで設定したアプリ名を使用するようにする

android/app/src/main/AndroidManifest.xml を編集して、 build.gradle で設定したアプリ名を使用するようにします。

AndroidManifest.xml
- android:label="flutter_app_template"
+ android:label="@string/app_name"

これで環境によってアプリ名が変わるようになりました。

アイコンを切り替えるためのタスクを追加する

flutter_launcher_icons パッケージを使って環境ごとのアイコンを作成しておきます。
src/{flavor}/res ディレクトリに複数の mipmap-xxx ディレクトリがあり、 ic_launcher.png が生成されています。

環境により、これらのアイコンを切り替えたいため、 build.gradle に以下を追記します。

// android/app/build.gradle

task copySources(type: Copy) {
    from "src/${dartDefines.flavor}/res"
    into 'src/main/res'
}

tasks.whenTaskAdded { task ->
   task.dependsOn copySources
}

ビルド時に環境ごとのディレクトリ内の ressrc/main/res にコピーしています。

以前のandroid.sourceSets.mainで複数のフォルダをres.srcDirsに指定する方法

src/main/res の中身を削除しても、 flutter create を実行すると、再度 src/main/res 内にアイコンファイル等が生成されてしまい、起動時にファイルの重複エラーが発生してしまうため、こちらの方法は使わないことにしました。


android/app/build.gradle
android {
    compileSdkVersion flutter.compileSdkVersion

    sourceSets {
        main {
            java.srcDirs += 'src/main/kotlin'
            res.srcDirs = ['src/main/res', "src/${dartDefines.flavor}/res"]
        }
    }
...

res.srcDirs を指定し、 src/main/ressrc/{flavor}/res をマージしています。
この時、この2つのディレクトリに同じファイルが存在するとエラーになるので、2箇所に同じファイルが存在しないようにしましょう。

@kurogoma4d さん、コメントありがとうございました!

Firebase対応 (Android)

android/app/src に各環境(Flavor)と同名のディレクトリ(フォルダ)を作成。

  • android/app/src/dev/ , android/app/src/stg/ , android/app/src/prod/

各Firebaseプロジェクトに作ったAndroidアプリ用の google-services.json をそれぞれのディレクトリに配置します。

android/app/build.gradle に追記

環境別ディレクトリに配置した google-services.jsonandroid/app にコピーするタスクを定義しています。

android/app/build.gradle
task selectGoogleServicesJson(type: Copy) {
    from "src/${dartDefines.flavor}/google-services.json"
    into './'
}

tasks.whenTaskAdded { task ->
    if (task.name == 'processDebugGoogleServices' || task.name == 'processReleaseGoogleServices') {
        task.dependsOn selectGoogleServicesJson
    }
}

.gitignore ファイルに google-services.json を追加

android/app/google-services.json は、ビルド時に生成されて環境ごとに上書きされるので、Git管理対象外にしましょう。

.gitignore
+ **/android/app/google-services.json

iOSアプリに必要な対応

Dart define を受け取る Pre Actionを追加

ビルド時に指定した --dart-define をiOSで受け取るために、ビルド直前に実行されるスクリプトを追加する必要があります。

もちろん、スクリプトをXcode上から直接書き込んでも良いですが、

  • Runner.xcscheme に改行がない状態で書き込まれるので差分が見にくい
  • コメント等で日本語を書くとエンコードされて読めない

というデメリットがあり、新規ファイルを作成して使うことで、好きなエディタ(VS Codeなど)のハイライト機能等を利用しながら編集できる利点もあります。

スクリプトファイル保存

ios/scripts/extract_dart_defines.sh というパスとファイル名で以下の内容の sh ファイルを保存します。

ios/scripts/extract_dart_defines.sh
#!/bin/sh

# Dart defineを書き出すファイルパスを指定します。
# ここでは `Dart-Defines.xcconfig` というファイル名で作成することにします。
OUTPUT_FILE="${SRCROOT}/Flutter/Dart-Defines.xcconfig"
# Dart defineの中身を変更した時に古いプロパティが残らないように、初めにファイルを空にしています。
: > $OUTPUT_FILE

# この関数でDart defineをデコードします。
function decode_url() { echo "${*}" | base64 --decode; }

IFS=',' read -r -a define_items <<<"$DART_DEFINES"

for index in "${!define_items[@]}"
do
    item=$(decode_url "${define_items[$index]}")
    # Dartの定義にはFlutter側で自動定義された項目も含まれます。
    # しかし、それらの定義を書き出してしまうとエラーによりビルドができなくなるので、
    # flutterやFLUTTERで始まる項目は出力しないようにしています。
    lowercase_item=$(echo "$item" | tr '[:upper:]' '[:lower:]')
    if [[ $lowercase_item != flutter* ]]; then
        echo "$item" >> "$OUTPUT_FILE"
    fi
done

XcodeのBuild Pre-actions に作成したスクリプトを登録する

  1. Xcodeで、Product > Scheme > Edit Scheme (⌘ ⇧ <)を開きます
  2. XcodeのScheme (Runner) をクリックして Edit scheme -> Build を展開して Pre-actions を選択します
  3. 「+」ボタンを押して「New Run Script Action」を選択します。
  4. 「Provide build settings from」は Runner を選択します。
  5. 先ほど保存したファイルのパスである ${SRCROOT}/scripts/extract_dart_defines.sh を書き込みます。

スクリプトファイルに実行権限を与える

そのままビルドしてもスクリプトファイルが実行されません。
以下のコマンドで実行限限を与えておきましょう。

chmod 755 ios/scripts/extract_dart_defines.sh

https://qiita.com/shisama/items/5f4c4fa768642aad9e06

各種xcconfigファイルで Dart-Defines.xcconfig をインポート

前項のスクリプトで生成される Dart-Defines.xcconfig がDebug, Releaseビルド両方で使われるように、既存の *.xcconfig ファイルでインクルードします。

ios/Flutter/Debug.xcconfig
 #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
 #include "Generated.xcconfig"
+ #include "Dart-Defines.xcconfig"
ios/Flutter/release.xcconfig
 #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
 #include "Generated.xcconfig"
+ #include "Dart-Defines.xcconfig"

.gitignore ファイルに Dart-Defines.xcconfig を追加

Dart-Defines.xcconfig は、ビルド時に生成され、環境により内容が上書きされるので、Git管理対象外にします。

.gitignore
+ **/ios/Flutter/Dart-Defines.xcconfig

アプリ表示名を環境によって変える

ios/Runner/Info.plist を編集します。

アプリ名に使われる CFBundleDisplayNameCFBundleName にアプリ名を指定します。

ios/Runner/Info.plist
 <key>CFBundleName</key>
- <string>flutter_app_template</string>
+ <string>$(appName)</string>
+ <key>CFBundleDisplayName</key>
+ <string>$(appName)</string>

環境ごとに以下のようなアプリ名が表示されるようになりました👍

  • dev: FAT dev
  • stg: FAT stg
  • prod: FAT

Bundle IDを環境によって変える

ios/Runner.xcodeproj/project.pbxprojPRODUCT_BUNDLE_IDENTIFIER で検索するか、
Xcode > Runner > TARGETS Runner > Build Settings の Product Bundle Identifier を表示して、
$(appId) に変更します。

Debug, Profile, Release すべてのBundle IDに $(appId) が設定されていることを確認してください。

ios/runner.xcodeproj/project.pbxproj
- PRODUCT_BUNDLE_IDENTIFIER = "jp.co.altive.fat";
+ PRODUCT_BUNDLE_IDENTIFIER = "$(appId)";

これで、環境ごとにアプリのBundle IDが変わるようになりました👏

Appアイコンを環境によって変える

ios/Runner.xcodeproj/project.pbxprojASSETCATALOG_COMPILER_APPICON_NAME で検索するか、
Xcode > Runner > TARGETS Runner > Build Settings の Primary App Icon Set Name を表示して、
AppIcon と指定されている値を AppIcon-$(flavor) に変更します。

忘れずに Debug, Profile, Release すべて共通の値になるようにしましょう。

# ios/runner.xcodeproj/project.pbxproj
- ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon";
+ ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-$(flavor)";

これで、環境ごとにアプリのアイコンが変わるようになりました👏

Firebase対応 (iOS)

Firebaseを使用していてかつ環境ごとにプロジェクトを分ける場合は、GoogleService-Info.plist を環境ごとに使い分ける必要があります。

ios ディレクトリに各環境(Flavor)と同名のディレクトリ(フォルダ)を作成。

  • ios/dev/ , ios/stg/ , ios/prod/

各Firebaseプロジェクトに作ったiOSアプリ用の GoogleService-Info.plist をそれぞれのディレクトリに配置します。

Select GoogleService-Info.plist

ビルド時に、環境に対応した GoogleService-Info.plist を使用できるようにするスクリプトを追加します。

  1. Build phases -> New run script を選択して新しい Run Script を追加
  2. 名前をわかりやすいように Select GoogleService-Info.plist に変更
  3. スクリプトを記述
  4. Output Files$(SRCROOT)/Runner/GoogleService-Info.plist を追加
  5. 既存Scriptである Copy Bundle Resources より上に移動
project.pbxproj
\cp -f ${SRCROOT}/${flavor}/GoogleService-Info.plist ${SRCROOT}/Runner/GoogleService-Info.plist

.gitignore ファイルに GoogleService-Info.plist 追加

ios/Runner/GoogleService-Info.plist は、ビルド時に生成されて環境により内容が変わるので、Git管理対象外にします。

.gitignore
+ **/ios/Runner/GoogleService-Info.plist

macOS対応

macOSの対応は、ほとんどiOSと同じです。
ios ディレクトリを macos ディレクトリに読み替えて同じように実行してください。

一部、異なる点を以下の通りです。

  • iOSの Debug.xcconfig は、macOSでは Flutter-Debug.xcconfig という名前です
  • iOSの Release.xcconfig は、macOSでは Flutter-Release.xcconfig という名前です

Flutterアプリを起動して、Flavorがきちんと伝わっているか確かめる

--dart-define-from-file がきちんとネイティブに伝わり、アプリ名やBundle IDがFlavorごとに変更されていることを手軽に確かめるためには、 package_info_plus パッケージを使用します。

https://pub.dev/packages/package_info_plus

PackageInfo の下記メソッドを使用して確認できます。

  • .appName
    • iOS: アプリ名 (CFBundleDisplayName)
    • Android: android:label="@string/app_name"
  • .packageName
    • iOS: Bundle ID (CFBundleIdentifier)
    • Android: applicationId

さいごに

最後まで読んでいただきありがとうございました😊

宣伝

Altive株式会社では、Flutterアプリの開発・運営を承っております。
お気軽にお問い合わせください🫡
https://altive.co.jp/contact


Riverpod の実践入門本を公開中です📘
https://zenn.dev/riscait/books/flutter-riverpod-practical-introduction

参考記事

https://itnext.io/flutter-1-17-no-more-flavors-no-more-ios-schemas-command-argument-that-solves-everything-8b145ed4285d
https://medium.com/flutter-jp/flavor-b952f2d05b5d
https://qiita.com/hiromasa-fun/items/c79c99535f6f1db2a6a9
https://zenn.dev/kingu/articles/9192b91062841b8e0bba
https://medium.com/flutter-jp/icon-935d637d2da0
https://neos21.net/blog/2020/09/17-02.html

GitHubで編集を提案
Altiveエンジニアリングブログ

Discussion

monomono

Flutter SDKのバージョンアップグレードで Dart-defines の暗号化方法が変わり、対応の必要が出てくる可能性がある

暗号化ではなく、エンコードです。
秘匿する目的ではなく、渡されたパラメーターの値を後から正確に展開するための処理のはずです。
暗号化と表現すると、ここにセキュアな値を入れても良いかのような誤解を招きそうですが、そうではないので。

akaboshinitakaboshinit

記事わかりやすく、とても参考になりました!
ありがとうございます🙇

自分が利用していて気づいたことなのですが
このDart-defineを使った上でfirebaseを新規導入する際は
Copy Bundle ResourcesにSelect GoogleService-Info.plistで
コピーされたGoogleService-Info.plistを追加しないと
GoogleService-Info.plistが読み込まれずfirebaseを使うことができないみたいです!
xcodeconfig

村松龍之介村松龍之介

@akaboshinit さん、追加情報ありがとうございます✨
遅くなってしまいましたが、記事に追記させていただきました🙌

ツルオカツルオカ

Flavor対応参考にさせていただきました。
--dart-defineが出る前の名残でflutter_flavorizr | Flutter Package辺りを使って凌いでましたが、記事の通り進めたらさくっとできました。ありがとうございます。

1点アプリアイコンの切り替えについてですが、出力までの手順に留まっており反映の記述が漏れている様子でしたので、間違いなさそうであれば追記お願いします🙏
(私の環境ではこちらの設定なしではアイコン反映できずでした)

村松龍之介村松龍之介

ツルオカさん、ご報告ありがとうございます🙏😊
後ほど追記させていただきます!

kboy (Kei Fujikawa)kboy (Kei Fujikawa)

Xcode上でRunnerディレクトリへ GoogleService-Info.plist ファイル(どの環境のものでも良い)をドラッグ&ドロップで追加してファイルと参照を追加しましょう。
Downloadフォルダ等から、Runnerフォルダ内へドラッグ&ドロップ (Copy items if needed にチェック)

ここに関してのコメントですが、Downloadフォルダ等からではなく、Runnerディレクトリにコピーされて出来上がったものをドラッグアンドドロップして参照するようにするのが良いと思います!(ダウンロードフォルダからとかだと参照のディレクトリが違くて、「Cannnot find ~/GoogleService-Info.plist」みたいなエラーが出そうです!)

村松龍之介村松龍之介

kboyさん、コメントありがとうございます😊
Xcodeでドラッグ&ドロップする際に、 Copy items if needed にチェックすれば、Downloadフォルダにあるファイルへの参照ではなく、新規にファイルを作成して配置してくれるので参照エラーは出ない認識でした…!
試してみて問題あるようだったら修正しますね💪

kboy (Kei Fujikawa)kboy (Kei Fujikawa)

Xcodeでドラッグ&ドロップする際に、 Copy items if needed にチェックすれば、Downloadフォルダにあるファイルへの参照ではなく、新規にファイルを作成して配置してくれるので参照エラーは出ない認識でした…!

なるほどです!そしたらコピーされるので大丈夫かもです!!

Daigo WakabayashiDaigo Wakabayashi

Flavor対応参考にさせて頂きました〜!
記事通り進めるだけで手軽にできて驚きました、ありがとうございます!!

Android Studio の設定例

TODO

もし良ければスクラップに残しておいたので使ってください!🎯

https://zenn.dev/mamushi/scraps/13c0232c88227e

村松龍之介村松龍之介

Daigoさん、サポートありがとうございます!!
またAndroid Studioの設定例もありがとうございます🙌
後ほど記事更新してリンクさせていただきます😊

Kurogoma4DKurogoma4D

久々にアイコン差し替えの対応をすることがあって、忘れてしまっていた部分が多かったので参考にさせていただきました 🙏

Androidのアイコン差し替えですが、設定でビルド時に src/main/ressrc/${flavor}/res をマージすることができるので、それを使ったほうが綺麗かなと思います。
https://developer.android.com/studio/build/build-variants?hl=ja#configure-sourcesets

  sourceSets {
    main {
      res.srcDirs = ['src/main/res', 'src/${dartEnvironmentVariables.FLAVOR}/res']
    }
  }
村松龍之介村松龍之介

ありがとうございます!!Androidの知見少ないのでありがたいです🙌
後ほど使わせていただくと思います!

kyam_28kyam_28

参考になる記事をありがとうございます!
launch.json の参考例ですが、余計な { が4行目に含まれているようなので一応記載しておきます!

tomotomo

記事とても参考になりました。ありがとうございます。

macOSでしか動作確認をしておりませんが、 Select GoogleService-Info.plistCompile Sources よりも上にしないと環境が適切に切り替わらなかったように見受けられました。もしどなたかのご参考になりましたら。

Kirk / Kazuki TataiKirk / Kazuki Tatai

大変参考になりました。ありがとうございます🙌

CFBundleDisplayName がiOS端末のホームアイコンに表示されるアプリ名となります。
CFBundleName の方はiOSに限れば使用されている箇所を見つけることができませんでした。

https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundlename

CFBundleName とは、と自身も改めて確認したところ

This name can contain up to 15 characters. The system may display it to users if CFBundleDisplayName isn't set.

とあったので CFBundleDisplayName があれば不要そうでした。自分は CFBundleDisplayName をセットしてCFBundleName を削除してみたところ、正常にビルドされました🙋‍♂️

村松龍之介村松龍之介

Kirkさん、ありがとうございます🙌
CFBundleName を削除しても問題なくビルド可能なんですね👏✨

もしかしたら環境によるかもしれませんが、 CFBundleName は出力した ipa ファイルの名前になるようです👀
CFBundleNamesample の場合は sample.ipa になる)

しかし、このとき CFBundleName に日本語が含まれていた場合省略されるようです。
CFBundleNameサンプルapp の場合、「サンプル」部分が省略されて app.ipa になる)

なので、 CFBundleDisplayName が日本語のアプリなどは分けて定義するメリットがあるかもしれませんね✍️

Kirk / Kazuki TataiKirk / Kazuki Tatai

わざわざコメントありがとうございます!
実はコメントしたあとに「Archive はどうなるんだ…」と気になってました。笑
さらに逆に勉強になりました。ありがとうございます🙏

よねよね

いつもこの記事のお世話になっております🙇

iosのGoogleService-info.jsonの位置がRnner/Runner配下でないと動かない仕様?になっている気がしましたがどうでしょうか?

\cp -f ${SRCROOT}/${FLAVOR}/GoogleService-Info.plist ${SRCROOT}/Runner/GoogleService-Info.plist

また、大変分かりやすいので、Firebase CLI版(androidは変わらず、iosのfile名が変わっただけかもですが...)も執筆していただけることを心待ちにしております!

JboyHashimotoJboyHashimoto

龍之介さん!

すみません、こちらのコマンドを打つ前にbuild.gradlededで、minSdkVersionに数値を入力しないとエラーが出てしまうようです!
最近は、Flutter SDK本体の設定をいじれば、数値を設定できていたのですが、flutter_launcher_iconsは違うみたいです!
驚きました!

flutter pub run flutter_launcher_icons:main

エラーログ

mcz9mmmcz9mm

非常に参考になり助かりました🙏
要ないかもしれませんが、dart-defineを外してビルドした際にデフォルトでdevのアイコンをセットできるようにもしてみたので共有です。

// android/app/build.gradle
def environment = dartEnvironmentVariables.FLAVOR
if (environment == null) {
    environment = "dev"
}
task copySources(type: Copy) {
    from "src/$environment/res"
    into 'src/main/res'
}
ios/scripts/retrieve_dart_defines.sh

~~~~~~~
        echo "#include \"$value.xcconfig\"" >> $OUTPUT_FILE
    fi
done
# 追記 ファイルが空の際にdevを指定する
if [ ! -s $OUTPUT_FILE  ] ; then
  echo "#include \"dev.xcconfig\"" >> $OUTPUT_FILE
fi
村松龍之介村松龍之介

とても遅くなってしまいましたが、 build.gradle の記法など勉強になります!
共有ありがとうございます🙌

いるかいるか

こちらの記事のおかげでアプリ名の出しわけができました!
とても役に立つ記事、ありがとうございます。

marrbormarrbor

前回記事からお世話になっています。ありがとうございます。
Select GoogleService-Info.plistのスクショで記入されているコピーコマンド、

cp -f ${SRCROOT}/${FLAVOR}/GoogleService-Info.plist ${SRCROOT}/GoogleService-Info.plist

cp -f ${SRCROOT}/${flavor}/GoogleService-Info.plist ${SRCROOT}/GoogleService-Info.plist
ですね(実際作業してたら間違えないと思いますが)。

また、Web (Firebase Hosting 使用)ですが、以下でちゃんと環境分けできてます。

> flutter build web --dart-define-from-file=/path/to/json
> firebase deploy --project プロジェクトID --only hosting
村松龍之介村松龍之介

遅くなりましたが、補足コメントありがとうございます🙌
flavor の大文字・小文字のミスですね。あとで修正いたします🙏

kenmakenma

役に立ちました。
extentionを使っていると、ビルド時に次のエラーで失敗します。

error: Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier.

この記事の方法とコメントで対応できました。

村松龍之介村松龍之介

遅くなりましたが、extensionを使用した場合の補足とコメントありがとうございます🙌

utamoriutamori

この記事の内容ですが、Flutter 3.17で動作しなくなる可能性が高そうです 😱
https://github.com/flutter/flutter/issues/136444#issuecomment-1819780732

nobu.nobu.

前回の記事からお世話になっています。ありがとうございます。

dart_defines/dev.env の googleReversedClientId の行の最後に「,」が入っていますが、「,」があると「"」と「,」が残ったまま環境変数が設定されてしまうようです。

googleReversedClientId の出力例

flutter: "com.googleusercontent.apps.0123456789-xxxxxxxxxxxxxxxx",

dart_defines のファイルは「,」を削除した以下の形が正しいかと思います。

flavor="dev"
appName="FAT dev"
appId="jp.co.altive.fat.dev"
googleReversedClientId="com.googleusercontent.apps.0123456789-xxxxxxxxxxxxxxxx"

google認証をしようとした際に、アプリがクラッシュされる方がいらっしゃったらこれが原因かもしれません。

村松龍之介村松龍之介

ご指摘ありがとうございます!JSONファイルの時の末尾カンマが残ってしまっていたみたいです…!
修正します🙌

RyotaRyota

この構成でプロジェクトを作った場合って、apiKeyなどのセキュアなものってどこで持たせるのがいいのでしょうか。そこだけ綺麗にできずに困ってます。

村松龍之介村松龍之介

秘密鍵などのセキュアなものはアプリに入れてしまうと、成果物から解読されるリスクが高い認識です。

なので、原則サーバーサイドで管理するのが良いかと思っています。
それがどうしても難しい場合は envied等を使って難読化するのが妥協案になりそうです。

RyotaRyota

返信ありがとうございます。
他の方の記事とかで.envにapiKeyを入れてる方がちらほらいたので気になりました。
privateのリポジトリにあげるとしてもアプリ自体を解析されてしまう可能性があるんですね。その辺の知識がなかったので勉強になりました。

K9i - Kota HayashiK9i - Kota Hayashi

build.gradleを編集してdart-defineを受け取るの部分でちょっとハマりそうな挙動を発見したので報告です。

こんな感じにvalueが空の変数を定義したときに

dart_defines/dev.env
hoge=""

pair.first()とpair.last()が両方hogeとなり、keyもvalueもhogeのmapになるみたいです。

android/app/build.gradle
// dart-define を入れる変数を宣言しています。
def dartDefines = [:];
if (project.hasProperty('dart-defines')) {
    // カンマ区切りかつBase64でエンコードされている dart-defines をデコードして変数に格納します。
    dartDefines = dartDefines + project.property('dart-defines')
        .split(',')
        .collectEntries { entry ->
            def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
            [(pair.first()): pair.last()]
        }
}

対策としてはlengthが2でない場合に空のmapにするなどが考えられそうです。

android/app/build.gradle
- [(pair.first()): pair.last()]
+ pair.length == 2 ? [(pair.first()): pair.last()] : [:]

以下は実際にこのケースに遭遇した際に対応したIssueです。
https://github.com/yumemi-inc/flutter-mobile-project-template/pull/233

村松龍之介村松龍之介

報告ありがとうございます🙌
なるほど!空文字の値を設定してしまうと、lastpair もKeyの方を参照してしまうんですね…!
勉強になりました✍️
後ほど対策取り入れさせていただくと思います🙏