LoginSignup
29
31

More than 3 years have passed since last update.

Flutterで新規アプリ作る時にやることまとめ

Last updated at Posted at 2020-06-02

Flutterで新規にアプリを作る時にテンプレ的に大体同じような事をやっているが、いつも忘れるので備忘録としてまとめ。

Flutterは絶賛開発中なので、本稿における記載は当時はこれでうまくいっていたぐらいに留めていただけると幸いです。
また、本稿は個人的なメモの意味合いが強いため、割愛している説明が多々あります。

Flutter2での動作確認と全編リファクタした新バージョンはこちら

環境

flutter --version

Flutter 1.22.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision d408d302e2 (5 weeks ago) • 2020-09-29 11:49:17 -0700
Engine • revision 5babba6c4d
Tools • Dart 2.10.0

Flutter Create

プロジェクトを作る。

flutter create --org com.example your_project_name

メモ

flutter create -hに書いてあるとおり、iOSとAndroidネイティブ実装側はSwiftKotlinがデフォルトで指定されるので、もし変更したい場合は--ios-language--android-languageで指定すること。

orgはパッケージ名に影響するため、後から変更するのは面倒。
特にandroid/app/src/main/kotlin/**のディレクトリにも影響するため、掃除が面倒なので後から変更するなら注意。

Firebase プロジェクト作る

Cloud FirestoreやAuthenticationを使わないからFirebase必要ない場合もあると思うが、CrashlyticsPerformanceはとても便利なので裏方としてだけでも導入するのはオススメ。

devやprodなど必要な環境分だけ作っておこう。
アプリを実行してインストールを確認とかいう疎通確認のステップはとりあえず無視。

デフォルトの GCP リソース ロケーション

大抵の場合は東京であるasia-northeast1で良いとは思う。

App Distribution

App Distributionを利用するつもりなら、コンソールからiOS/Android各アプリ毎に開始をクリックしておくことを忘れずに。
予め開始しておかないとアップロードする際にエラーが出る

Error: App Distribution could not find your app 1:hoge:ios:hoge. Make sure to onboard your app by pressing the "Get started" button on the App Distribution page in the Firebase console: https://console.firebase.google.com/project/_/appdistribution

構成ファイル

構成ファイルはブラウザからポチッとダウンロードしてもいいが、firebaseコマンドでダウンロードもできるので、スクリプトを書いておくと楽。
(このスクリプトでは後述するFlavor対応のために適切なディレクトリとファイル名で配置するようにしている。)

Androidの署名

keystoreはGUIから生成してもいいし、android - How can I create a keystore? - Stack Overflowを参考にして生成してもよい

例えば
keytool -genkey -v -keystore release.keystore -alias key0 -keyalg RSA -keysize 2048 -validity 10000
keytool -importkeystore -srckeystore release.keystore -destkeystore release.keystore -deststoretype pkcs12

android/app/build.gradleで指定することになるので、android/app/release.keystoreに置くとよい。

SHAはAuthenticating Your Client  |  Android 用 Google API  |  Google Developersなどを参考にして確認して、Firebaseに登録しておく。

例えば
keytool -list -v -alias key0 --keystore release.keystore

build.gradle

signingConfigsにkeystoreを追記し、releaseビルド時に利用する。
keystoreのパスワードは必要に応じて隠そう。
参考: 今更ながらAndroid の keystore と 署名(signingConfigs) の管理・運用について考えてみた - Qiita

android/app/build.gradle
    signingConfigs {
        release {
            storeFile file("release.keystore")
            storePassword "android"
            keyAlias "key0"
            keyPassword "android"
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }

Flavor

dart-defineを使った方法を紹介
--flavorを使った場合よりも簡単?)

Android

アプリ名の部分を@string/app_nameに修正

android/app/src/main/AndroidManifest.xml
<application
    android:label="@string/app_name">

dart-defineからの入力をパースしてアプリケーションIDのSuffixとアプリ名にセットする

android/app/build.gradle
def dartEnvironmentVariables = [
        APP_NAME: 'デフォルト',
        APP_SUFFIX: null,
        APP_ENV: 'default'
]

if (project.hasProperty('dart-defines')) {
    dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines')
            .split(',')
            .collectEntries { entry ->
                def pair = URLDecoder.decode(entry).split('=')
                [(pair.first()): pair.last()]
            }
}
android {
...
    defaultConfig {
        applicationIdSuffix dartEnvironmentVariables.APP_SUFFIX
        resValue "string", "app_name", dartEnvironmentVariables.APP_NAME
    }
...
}

Firebaseを利用するなら

FirebaseのSDKはandroid/app/google-services.jsonを自動で読み込むのでFlavorに応じて差し替えてあげる。

まずAndroid Installation | FlutterFireに従ってandroid/build.gradleなどに依存を追加していく。

次に以下のタスクを追加して、Flavorに応じたgoogle-services.jsonを配置する。

android/app/build.gradle
task copyGoogleServicesJson(type: Copy) {
    from "google-services-${dartEnvironmentVariables.APP_ENV}.json"
    into './'
    rename "(.+)-${dartEnvironmentVariables.APP_ENV}.json", '$1.json'
}

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

最後にandroid/app/google-services.jsonはFlavorを変える毎に変更されるのでgitignoreに追加しておく。

echo "android/app/google-services.json" >> .gitignore

iOS

Info.plistにAPP_NAMEをセット。
PRODUCT_NAMEにしておくとexportする際にRunner.ipaで出力される

ios/Runner/Info.plist
        <key>CFBundleName</key>
-       <string>アプリの名前</string>
+       <string>$(PRODUCT_NAME)</string>
        <key>CFBundleDisplayName</key>
-       <string>$(DISPLAY_NAME)</string>
+       <string>$(APP_NAME)</string>

Build SettingsからProduct Bundle Identifierを検索して以下のようにAPP_SUFFIXをセット

com.example.app$(APP_SUFFIX)

つまり以下のような変更になる

ios/Runner.xcodeproj/project.pbxproj
- PRODUCT_BUNDLE_IDENTIFIER = com.example.app;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.app$(APP_SUFFIX)";

次にRunnerのSchemeをEditして、BuildのPre-actionsにflutter 1.19 parse sh scriptをコピペする。
最後の処理のxcconfigに書き出す部分は適切に修正すること。

今回の例だと
printf "%s\n" "${define_items[@]}"|grep '^APP_' >> ${SRCROOT}/Flutter/Generated.xcconfig

Provide build settings fromRunnerを選択

Firebaseを利用するなら

FirebaseのSDKはios/Runner/GoogleService-Info.plistを自動で読み込むのでFlavorに応じて差し替えてあげる。

まず適当な内容でGoogleService-Info.plistを作って、XcodeのProject NavigatorRunnerディレクトリ下にドラッグアンドドロップしてGoogleService-Info.plistへの参照を作っておく。
参考: iOS Installation | FlutterFire

echo '
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>
' > ios/Runner/GoogleService-Info.plist

Build PhasesNew Run Script PhaseGoogleService-Info.plistをFlavor毎に切り替えるRunScript dart-defineを使った場合をコピペ。

作ったRun ScriptのOutput Filesに以下を追加

$SRCROOT/Runner/GoogleService-Info.plist

作ったRun ScriptはCopy Bundle Resourcesよりも上に配置する。

各FlavorのGoogleService-Info.plistは以下のように配置しておく

  • ios/Runner/GoogleService-Info-dev.plist
  • ios/Runner/GoogleService-Info-prod.plist

最後にios/Runner/GoogleService-Info.plistはFlavorを変える毎に変更されるのでgitignoreに追加しておく。

echo "ios/Runner/GoogleService-Info.plist" >> .gitignore

Runner.entitlements

Flavor毎にApp GroupsやAssociated Domainsを切り替える場合、./ios/Runner/Runner.entitlementsには次のように変数にしておき、dart-defineで渡す

./ios/Runner/Runner.entitlements
    <key>com.apple.developer.associated-domains</key>
    <array>
        <string>${APP_ASSOCIATED_DOMAIN}</string>
    </array>

Flutter

以下のようにしてdart-defineで値を渡す
APP_NAME="devアプリ"のようにすると"まで渡されてしまうので注意)
APP_SUFFIXが不要の場合は--dart-define APP_SUFFIX=""ではなく削除する)

flutter build ios \
--dart-define APP_NAME=devアプリ \
--dart-define APP_SUFFIX=.dev \
--dart-define APP_ENV=dev

Flutter側で値を使いたいならこのようにする。(finalではなくconstにすること)

  const appName = String.fromEnvironment('APP_NAME', defaultValue: 'unknownName');
  const appSuffix = String.fromEnvironment('APP_SUFFIX', defaultValue: 'unknownSuffix');
  const appEnv = String.fromEnvironment('APP_ENV', defaultValue: 'unknownEnv');

AndroidStudio

所定のRun/Debug ConfigurationsAdditional argumentsに以下のように追記して値を渡す
APP_NAME="devアプリ"のようにすると"まで渡されてしまうので注意)
APP_SUFFIXが不要の場合は--dart-define APP_SUFFIX=""ではなく削除する)

--dart-define APP_NAME=devアプリ --dart-define APP_SUFFIX=.dev --dart-define APP_ENV=dev

runConfigurationsの例

Configurationsもコミットする場合はgit add --forceが必要。
flutter createではデフォルトで.idea/がgitignoreされているので手動で追加する必要がある)

git add --force .idea/runConfigurations/*.xml

参考文献

輸出コンプライアンス

「いいえ」と答える場合はInfo.plistにあらかじめ追記しておくと手動でいいえする必要がないので楽。

ios/Runner/Info.plist
+   <key>ITSAppUsesNonExemptEncryption</key>
+   <false/>

スクリーンショット 2020-12-16 10.08.09.png

FlutterFire

導入したいPluginを選ぼう!!!
https://firebase.flutter.dev/

導入方法は各PluginのREADMEが最新で正確なので割愛。

Improve iOS Build Times

iOSのFirestore SDKはC++で50万行あるらしいので、普通に参照するとビルドに時間がかかる。
そこでプリコンパイルされたバージョンを参照すると大幅にビルド時間を短縮できるのでおすすめ。
FlutterFire Overview | FlutterFire

コードのカバレッジ計測

コードカバレッジ計測する場合、以下のように--coverageを付与すればlcov.infoが出力される

flutter test --coverage --coverage-path=coverage/lcov.info

ただし、これはテストの対象になっているコードの結果のみが出力されるため、テストコードを全く書いていないとlcov.infoには何も出力されない。

そこで、全部のdartファイルをimportするテストコードを用意しておけば、flutter test --coverageした時に全てのコードがlcov.infoに含まれるようになる

test/coverage_test.dart
import 'package:example_app/hoge.dart';

void main() {}

手動でやるのは面倒なのでヘルプスクリプトで生成する

curl https://raw.githubusercontent.com/KoheiKanagu/dart_full_coverage/master/dart-coverage-helper | sh

gitignoreもしておく

echo "lcov.info" >> .gitignore

参考

Flutter test coverage will not report untested files · Issue #27997 · flutter/flutter
How can I generate test coverage of untested files on my flutter tests? - Stack Overflow
priezz/dart_full_coverage: Helper for full tests coverage checkup for you Dart/Flutter project

CICDについて

Github Actionsでの例を示す。
条件として、AndroidのビルドはGithubのubuntu-latest、iOSのビルドは私物のMacでself-hostedを利用する。
(Androidもself-hostedにしたければ指定するだけで対応できる)

そのため、iOSの証明書などはself-hostedのMacでよしなに整備されているものとする。
(要はプライベートリポジトリでGithub Actions使いたいけどmacOSのランナーはLinuxに比べてそこそこのお値段だから使ってないMacを活用しつつコストダウンという算段)

ジョブの流れ

詳細は割愛するが、git-flowに合わせる

pull_requests.yml
1. プルリク作成/更新されたら開始
2. テスト実行
3. Codecov
4. Slackで通知

releases.yml
1. プルリクがmasterかdevelopにマージされたら開始
2. テスト実行
3. Codecov
4. バージョンのタグを貼る
5. Releaseを作成
6. release/*ブランチからmasterブランチにマージする際にはdevelopブランチに#minorと空コミット
7. Slackで通知
8. ビルド(devのiOS・devのAndroid・prodのiOS・prodのAndroidをパラレルに実行)
9. Release Assetに成果物(apk、aab、xcarchive、prodのipa)をアップロード
10. App Distributionで配布
11. Slackで通知

TODOとしている部分には適切な値を入れること。
2つとも.github/workflows/に配置する

bumping (releases.ymlのステップ6について)

タグを振るアクションの機能で、コミットのコメントに#minorなどと書くとbumpしてくれるので、リリースしたタイミングでdevelopに#minorとコミットすることでbumpされる
anothrNick/github-tag-action: A Github Action to tag a repo on merge.

Secrets

リポジトリのSecretsに以下をセットしておく

ビルドスクリプト

プロジェクトルートにscriptsというディレクトリを作って中に以下のファイルを入れておく(shは実行権限を忘れずにchmod +x ./scripts/*.sh

なおbuildiOS.shとbuildAndroid.shにおいては、APP_NAMEAPP_SUFFIXをよしなに変更すること。また、APP_ASSOCIATED_DOMAINは必要であれば。

AdHocExportOptions.plistとAppStoreExportOptions.plistにおいてはteamIDをよしなに変更すること。

Codecov

プロジェクトルートにcodecov.ymlを配置すれば、カバレッジには含めないファイルを指定できるなど、いろいろ設定できる。
例えば https://github.com/KoheiKanagu/my_flutter_app_template/blob/master/codecov.yml

workflow_dispatchでビルド

何らかの理由で手動でビルドしたい場合のジョブ
build_app_with_workflow_dispatch.yml

成果物の出力などはせず、ただビルドするだけ

アプリの標準言語を日本語にする

Xcodeでアプリの標準言語を日本語にする方法 - Qiita

記事ではdevelopmentRegionJapaneseにとあるがjaにすること

ios/Runner.xcodeproj/project.pbxproj
- developmentRegion = en
+ developmentRegion = ja

localizationsDelegates

GlobalWidgetsLocalizationsなどを指定するならpubspec.yamlに追記しておく

main.dart
    return MaterialApp(
      localizationsDelegates: const [
        GlobalCupertinoLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
    );
pubspec.yaml
dependencies:
  flutter_localizations:
    sdk: flutter
  intl: any

Firebase CrashlyticsのdSYM

dSYMをアップロードするスクリプト
uploadSymbols.sh

参考

可読化されたクラッシュ レポートを取得する  |  Crashlytics  |  Firebase

29
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
31