⚒️

Flutter の環境分けの新時代

2022/12/05に公開
4

Flutter Advent Calendar 2022 の 5 日目の記事です。


はじめに

みなさん、Flutter の環境分けはどのようにされていますか?

多くの方は、公式で紹介されている flavor を使う方法や --dart-define を使う方法を使用しているのかなと思います。

https://docs.flutter.dev/deployment/flavors

https://zenn.dev/altiveinc/articles/separating-environments-in-flutter-old-edition

今回、紹介するのは Flutter の 3.7.0 で導入された --dart-define-from-file を使う方法です。

--dart-define-from-file が導入された背景

--dart-define を使う方法だと次のような課題がありました。

  • 多くの定義がある場合、起動コマンドが非常に長くなってしまう
  • 切り替えるパッケージが複数ある場合、保守が困難になる
  • これらの定義を Android と iOS で直接利用しようとすると、それぞれで Base64 でデコードしなければならない

これらの課題を解決するために導入されました。

関連する Issue

https://github.com/flutter/flutter/issues/107810

https://github.com/flutter/flutter/issues/114296

関連する Pull Request

https://github.com/flutter/flutter/pull/108098

https://github.com/flutter/flutter/pull/114297

--dart-define-from-file の使い方

サンプルプロジェクト

https://github.com/blendthink/flutter-mobile-template

Flutter をインストール

--dart-define-from-file は、3.7.0 で正式にリリースされた機能です。
そのため、3.7.0 以上のバージョンを使用する必要があります。

次のコマンドを実行します。

# プロジェクトフォルダーを作成して移動
mkdir flutter-mobile-template
cd flutter-mobile-template

# Homebrew をインストール
# https://brew.sh/index_ja
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# FVM をインストール 
# https://fvm.app/docs/getting_started/installation
brew tap leoafarias/fvm
brew install fvm

# Flutter をインストール
fvm install 3.7.0
fvm use 3.7.0 --force
fvm flutter --version

# バージョン管理に含めないように .gitignore を作成
echo "flutter_sdk" > .fvm/.gitignore

プロジェクト作成

プロジェクトフォルダーで次のコマンドを実行します。

fvm flutter create \
    -t app \
    -i swift \
    -a kotlin \
    --platforms ios,android \
    --org dev.blendthink \
    --project-name flutter_mobile_template \
    .

設定ファイルを作成

それぞれの環境ごとの設定を定義します。

flavor/dev.json
{
  "flavor": "dev",
  "appName": "dev-Template",
  "appId": "dev.blendthink.template",
  "appIdSuffix": ".dev"
}
flavor/stg.json
{
  "flavor": "stg",
  "appName": "stg-Template",
  "appId": "dev.blendthink.template",
  "appIdSuffix": ".stg"
}
flavor/prd.json
{
  "flavor": "prd",
  "appName": "Template",
  "appId": "dev.blendthink.template",
  "appIdSuffix": ""
}

VS Code の設定ファイルを作成

Flutter SDK のパスを設定して、3.7.0 が使用されるようにします。

.vscode/settings.json
{
    "dart.flutterSdkPath": ".fvm/flutter_sdk",
    "search.exclude": {
        "**/.fvm": true
    },
    "files.watcherExclude": {
        "**/.fvm": true
    }
}

それぞれの環境ごとのデバッグ構成を定義します。

https://code.visualstudio.com/docs/editor/debugging

.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "dart",
            "request": "launch",
            "name": "dev-debug",
            "args": [
                "--dart-define-from-file=flavor/dev.json"
            ]
        },
        {
            "type": "dart",
            "request": "launch",
            "name": "stg-debug",
            "args": [
                "--dart-define-from-file=flavor/stg.json"
            ]
        },
        {
            "type": "dart",
            "request": "launch",
            "name": "prd-debug",
            "args": [
                "--dart-define-from-file=flavor/prd.json"
            ]
        }
    ]
}

Android の設定

android/app/build.gradle
 apply plugin: 'com.android.application'
 apply plugin: 'kotlin-android'
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 @@ -43,14 +47,13 @@ android {
     }

     defaultConfig {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "dev.blendthink.flutter_mobile_template"
         // You can update the following values to match your application needs.
         // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
+        applicationId appId
+        applicationIdSuffix appIdSuffix
         minSdkVersion flutter.minSdkVersion
         targetSdkVersion flutter.targetSdkVersion
         versionCode flutterVersionCode.toInteger()
         versionName flutterVersionName
+        resValue "string", "app_name", appName
     }
android/app/src/main/AndroidManifest.xml
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="dev.blendthink.flutter_mobile_template">
    <application
-        android:label="flutter_mobile_template"
+        android:label="@string/app_name"
         android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher">
         <activity

iOS の設定

ios/Runner/Info.plist
 	<key>CFBundleDevelopmentRegion</key>
 	<string>$(DEVELOPMENT_LANGUAGE)</string>
 	<key>CFBundleDisplayName</key>
- 	<string>Flutter Mobile Template</string>
+ 	<string>$(appName)</string>
 	<key>CFBundleExecutable</key>
 	<string>$(EXECUTABLE_NAME)</string>
 	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>6.0</string>
 	<key>CFBundleName</key>
ios/Runner.xcodeproj/project.pbxproj
@@ -365,7 +365,7 @@
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               PRODUCT_BUNDLE_IDENTIFIER = dev.blendthink.flutterMobileTemplate;
+                               PRODUCT_BUNDLE_IDENTIFIER = "$(appId)$(appIdSuffix)";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
                                SWIFT_VERSION = 5.0;
@@ -494,7 +494,7 @@
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               PRODUCT_BUNDLE_IDENTIFIER = dev.blendthink.flutterMobileTemplate;
+                               PRODUCT_BUNDLE_IDENTIFIER = "$(appId)$(appIdSuffix)";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
                                SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -517,7 +517,7 @@
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               PRODUCT_BUNDLE_IDENTIFIER = dev.blendthink.flutterMobileTemplate;
+                               PRODUCT_BUNDLE_IDENTIFIER = "$(appId)$(appIdSuffix)";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
                                SWIFT_VERSION = 5.0;

アプリ実行

VS Code の Run and Debug から実行します。

ぱっと見では確認しづらいため、サンプルプロジェクトでは次のステキなパッケージを利用させていただきました✨

https://pub.dev/packages/package_info_plus

https://pub.dev/packages/simple_logger

サンプルプロジェクトで確認したところ、次のようにログが出力されました🎉

flutter: 👻 INFO 2022-12-05 10:23:44.288258 [package:flutter_mobile_template/main.dart 9:10 in main] PackageInfo(appName: dev-Template, buildNumber: 1, packageName: dev.blendthink.template.dev, version: 1.0.0, buildSignature: , installerStore: com.apple.simulator)

おわりに

いかがでしたでしょうか?

個人的には、とてもメリットが多いと感じたため、正式リリースされたらこちらの方法に乗り換えようかなと思いました。

みなさんもぜひお試しください。

おまけ

タイトルは、ベストアーティスト2022に選出された Ado さんの「新時代」からつけました。

https://www.youtube.com/watch?v=nGsdH78_pXY

https://www.youtube.com/watch?v=1FliVTcX8bQ

個人的にはとても好きな曲で、作業中によくリピート再生して聴いています。
もし良ければ、みなさんもぜひ聴いていただければ幸いです。

GitHubで編集を提案

Discussion

akaboshinitakaboshinit

分かりやすい記事ありがとうございます!!

個人的に気になったのがjsonファイル内の変数は予約語ではないっぽいので
同じ意味になって、より簡単に記入できる方法が有るかなと思いました!
以下のスクラップにまとめてます!
https://zenn.dev/link/comments/1a2f8c7edb4552

おかやまんおかやまん

コメントありがとうございます🥰

jsonファイル内の変数は予約語ではない

ご認識の通りです🙆‍♂️

Android の Application ID と iOS の Bundle identifier は若干構成の仕方(サフィックスの付け方)が異なったり、Android と iOS で違う ID にしたいケースなどもあったりするため、あえて分けていました。

ただ、この記事のキー名では↑の意図が伝わりづらいと思いましたので、後ほど修正予定です💡

おかやまんおかやまん

appIdSuffix.dev のようにドットを含めたら同じような指定方法にすることができたので、そのように修正いたしました!

ご指摘のおかげで見直す機会ができ、このようにスッキリ書くことができることに気づくことができました。
改めましてありがとうございました 🥰

Android の Application ID と iOS の Bundle identifier は若干構成の仕方(サフィックスの付け方)が異なったり、Android と iOS で違う ID にしたいケースなどもあったりするため、あえて分けていました。
ただ、この記事のキー名では↑の意図が伝わりづらいと思いましたので、後ほど修正予定です💡

前回、↑のように言っていましたが、サフィックスが付いているということが伝われば良いかなと思いこのように修正しました(できれば共通の appId を使用していることも分かりやすくできれば良いのですが、いい方法が思いつかず、、)

↓差分も貼り付けておきます
https://github.com/blendthink/zenn/commit/412bbcc89d23e6d0f5021fe10642d7237142b1a7