約3年間Flutter で開発してきてのあれやこれや
この記事は株式会社TORICO Advent Calendar 2021 の10日目の記事です。
約一年ぶりくらいの記事になります。
去年や一昨年はFlutter 関係で色々な記事を書いていましたが、最近では状態管理の方法もProvider やReverpod で落ち着いていたり、プラグインも増えて色々なことができるようになってあまり記事を書いていませんでしたが、ここ1年2年で気がついたことや変わったことについて書いていけたらいいな〜と思います。
深夜に走り書きでかきあげました、typo などがあればコメントで教えていただけますと幸いです。
CI/CD について
私が最近開発を担当しているアプリではGitHub Actions とCodemagic を使用しています。
特に特殊な構成ではないので、目新しさはないかもしれませんが、色々と考えていわゆるDXの向上に努めています。
Github Actions
Github Actions ではPush のたびに dart analyze .
とdart format --fix -l 100 .
を実行しています。
Github Actions とFlutter の組み合わせの場合、 subosito/flutter-action を使っている方が多いかと思いますが、本プロジェクトではsubosito/flutter-action
を使用していません。
Flutter の公式ドキュメンに記載されているようにFlutter のインストールはGitHub から行えます。
git clone https://github.com/flutter/flutter.git -b stable
でインストールできること利用してubuntu 上にFlutter をインストールして analyze やformat を実行しています。
サンプルとして実際に動作しているものを掲載しておきます。よかったら参考にしてください。
name: Lint & Analyze
on:
push:
paths-ignore:
- "**.md"
jobs:
analyze:
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- name: Checkout pushed commit
uses: actions/checkout@v2
with:
token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }}
submodules: true
ref: ${{ github.event.pull_request.head.sha }}
- name: "Install Flutter"
run: ./.github/workflows/scripts/install-flutter.sh stable
- name: "flutter pub get"
run: flutter pub get
- name: Analyze
run: dart analyze . --fatal-infos
- name: Format
run: dart format --fix -l 100
上記のような書き方は flutterfire を参考にしています。
Flutter で有名なライブラリなどは色々なツールを使っていたり、ハッと思わされるような書き方をしているので、一度見てみても面白いかもしれません。
上記のまま導入して実行してもinstall-flutter.sh に実行権限が無いため、ワークフローが失敗します。
git update-index --add --chmod=+x install-flutter.sh
で実行権限を付与して commit とpush をしてください。
Codemagic
Codemagic はGitHub 上でリリースタグが作成されると自動的にビルドしてAppStoreConnectとGooglePlayConsole 上にアップロードされるようになっています。
社内テスト用として、TestFlightや Firebase App Distribution を使用していますが、テスト用に検証環境に接続しているアプリをビルドするといったworkflow は準備していません。
QAの方や運用の方に確認していただく際にはTestFlightや Firebase App Distribution に公開してインストールしていただいています。
普段はほぼ一人で開発しているため手元でビルドして実機にインストールしステークホルダーの方に確認していただくといったフローで十分なため、テスト用ににビルドするといったworkflow は準備していません。
複数人いて色々なビルドを作る必要がある場合はPRを作るたびにビルドを実行するWorkflow を用意してもいいかもしれません。
また、弊社でCodemagic を使用している理由としては他の方が開発に携わった際にバージョンとビルド番号に齟齬がないようにといった理由で採用しています。
互換性を担保するためにビルド番号でサーバーからのレスポンスが変わることがあるので想定したビルド番号より小さいと機能が足りなかったり、逆に大きいと対応出来ていない機能が露出してしまい不具合やお問い合わせに繋がる可能性があるため、ビルド番号の採番はCodemagic に一任しているといった形です。
ビルド番号はgit の全プランチの総コミット数 + プレフィックスで採番しているので、手元でビルドしてもCodemagic のワークフローの順番にビルドしていけば事故にならない想定ではありますが...
Codemagic上でgit の全プランチの総コミット数 + プレフィックス でビルド番号を採番する際に気をつけていただきたいのが、Codemagic ではビルドするプロジェクトをGitHub などからclone しますが、その際に--depth=1
がついています。
--depth=1
がついていると、最新のコミットしか取得しないため、ビルド番号が 1 + プレフィックスになってしまいます
そのため、コミットを全件取得するようにするか、github でタグを切った際にビルド番号を採番してpubspec.yaml に反映する必要があります。
マルチパッケージ
melos
弊社のほとんどのプロジェクトは何かしらの社内の共通ライブラリに依存しています。
それはFlutter のプロジェクトも然りです。
Flutter でライブラリを使用するにはpubspec.yaml
にライブラリを追加すると使用できます。
追加の方法は複数あり、pub.dev に公開する、git のリポジトリを指定する、path で同じプロジェクト内にあるライブラリを指定するなどがあります。
3パターンを書き出すと下記のような感じです。
dependencies:
flutter:
sdk: flutter
packaeg1: ^1.0.0 # pub.dev に公開
package2: # git 経由
git:
url: https://...
ref: ^1.0.0
package3: # 同一プロジェクト内に配置
path: ../package3
以前はgithub のプライベートリポジトリを指定して使用していましたが、
最近はpath で指定する方法を好んで使用しており、ディレクトリ構成は下記のようになります。
flutter_app/
packages/
flutter_app/
lib/
tests/
pubspec.yaml
commons/
lib/
payment_module/
lib/
path で指定すると1つ問題が発生します。
その問題とは各ライブラリに依存関係を追加したりすると各ライブラリのプロジェクトルートに行ってflutter pub get
をする必要があるということです。
pub.dev とgit で指定した場合はプロジェクトルートでflutter pub get
するとライブラリのインストールが完了しますが、path で指定している場合は各ライブラリのルートに行きflutter pub get
する必要があります。
依存関係が少ない場合には、ディレクトリを移動してflutter pub get
するだけのスクリプトを書いてもいいかもしれませんが複数のライブラリに依存するとめんどくさいです。
そういった際に melos があるととても便利です。
melos とは複数のパッケージ(monorepo)を管理するためのCLIツールです。
melos は始めに melos bootstrap
というコマンドを実行する必要があります。
melos bootstrap
を実行するとmelos がプロジェクト内のpubspec.yaml を探しだし各プロジェクトをmelos CLI で操作できるようになります。
たとえは下記のようなスクリプトを用意した際に melos run pub:get
を実行するとpackages内にある各ライブラリでflutter pub get
が行われるようになります。
packages:
- packages/**
scripts:
pub:get:
run: |
melos exec -c 1 \
"flutter pub get"
melos のコマンドは複数ありここでは説明しないので気になった方はぜひmelos のドキュメントを読んで見てください。
ライブラリの指定方法に戻りますが、path で指定した際に一番良いと感じることはライブラリの更新がとても楽だということです。
以前はgit で指定する方法を使用していましたがgit 経由でインストールしている場合、コードの変更のたびにgit commit & push
と flutter pub get
が必要になります。
melos が出てきたおかげてpath で指定して管理することがとても容易になり、ライブラリのコードを変更し保存するとHotReload が実行されアプリに反映されるといったFlutter のメリットも最大限活かして開発することが出来ています。
マルチパッケージにしている方は少ないかもしれませんが、アプリが大きくなってきた際にはアプリを分割してぜひmelos を使ってみてください。
Flutter のバージョン管理について
fvm
弊社ではできるだけStable チャンネルの最新版を使おうとしており、nullsafty に書き換えたプロジェクトのありますが、まだ対応が完了していないプロジェクトもあります。
そういったプロジェクトでは fvm を使用してFlutter のバージョン切り替えを行っています。
asdf も一時期採用していましたが何かしらの理由で fvm に戻っています。
Flutter のバージョン管理をfvm で行っていると書きましたが、Flutter はまだまだ成長段階で先日リリースされたFlutter 2.8 でもパフォーマンスが向上したり、Dartの新しい構文が取り入れられたりとアップデートすることにメリットがあると考えているので理想系は特定のチャンネルの最新版を追い続けることだと思っています。
fvm を使って安定した開発を行うことも良いですが、fvm を使わず最新版に上げ続けることも良いのではないでしょうか?
タスクランナー
Grinder
melos を使い初めてビルドや build_runner の実行はもっぱら melos で行いますが、melos ではシェルスクリプト で書くことになってしまうのである程度行数があったり、ファイル操作が必要な処理に関してはGrinder を使用しています。
用途としてはビルド番号を採番してpubspec.yaml のversion を書き換えたり、テスト実行して特定のファイルをカバレッジの結果から取り除いたりといった感じで使用しています。
Grinder だと引数を渡せたりするのもいいですね!
ただGrinder もMelos も標準入力があると動作してくれないので、標準入力がある処理はDart で書いたスクリプトも用意しています。
Grinder については過去に記事を書いているのでサンプルコードなどは下記のリンクより確認してみてください!
修正情報
- 2021/12/14
typo や諸々の修正
Discussion