Zenn

Flutterへのコントリビューション仲間を増やしたい

2024/11/25に公開
1

FlutterはGoogleが中心となって開発しているプロジェクトです。そのため、Flutterのソースコードを改善したり、バグを報告することは難しいイメージがあるのではないでしょうか?

次のPRを見てみてください。どちらも筆者が提出したPRになります。

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

https://github.com/flutter/website/pull/11020

いかがでしょうか? どちらも「簡単じゃん!」と思ったのではないでしょうか? そうです、Flutterへのコントリビューションは簡単なのです!
また「Flutterのコードだけど、Flutterのコードではないじゃん!」と思った方向けに、筆者の手に余りつつも、レビュアーの力を借りてPRをマージまで進めることができたPRも紹介しましょう。順調にいけば、Flutter 3.27にてリリースされる予定の修正です。

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

テストコードが長く見えますが、読んでみると、そう複雑なことをしているわけではありません。修正内容についても、Unicodeの仕様を反映しただけになります。
もちろん難しい修正もたくさんありますが、難しい修正だけがFlutterへのコントリビューションではありません。


本記事では、Flutterにコントリビューションするための環境構築について解説します。対象はflutter/flutterflutter/engineです。本記事は公式ドキュメントの内容を元に、筆者の経験を交えて記述しています。公式ドキュメントも十分に短い解説になっているので、気になる方はそちらも参照してください。

なおflutter/packagesも含めるか迷ったのですが、少しリポジトリの雰囲気が異なるため、今回は対象としません。flutter/packagesへのPRについては、より一般的なOSSへのコントリビューションに近いと思ってください。[1]

flutter/flutter

https://github.com/flutter/flutter

Flutterの中心的なリポジトリです。普段記述するWidgetたちはここにいます。また。ビルドやテストに利用するツールもいます。
普段利用している"Flutter"の改修を目指す場合には、このリポジトリのpackages/flutterを修正することになります。

この記事では、筆者の経験をもとに環境構築の手順を紹介します。ただengineを編集しない場合には、この記事と公式ドキュメントの難易度はほぼ同じです。

https://github.com/flutter/flutter/blob/master/docs/contributing/Setting-up-the-Framework-development-environment.md

実はFlutterアプリを書いている皆様は、このリポジトリをclone済みです。というのも、Set up Flutterで開発マシンに落としているのが、flutter/flutterリポジトリであるためです。flutter channelflutter upgradeのコマンドは、gitで言えばflutter/flutterのbranchやtagを切り替えることで実現されています。

確かめてみましょう。筆者は~/flutterに開発用のSDKをインストールしています。次のようにディレクトリを移動して、git remote -vを実行すると、以下のような出力が得られます。
ぜひ、自分の環境でも確認してみてください。

$ cd ~/flutter
$ git remote -v
origin	https://github.com/flutter/flutter.git (fetch)
origin	https://github.com/flutter/flutter.git (push)

このリポジトリをcloneするとFlutter SDKとして使える仕組みは、flutter/flutterの変更を手元で試しやすく、PRを作成しやすいというメリットにつながります。[2] このメリットを活かして、flutter/flutterへのコントリビューションの準備を進めていきましょう。


flutter/flutterのPRを作成する場合、次の手順が必要です。一部をスキップしたり追加したりすることもありますが、基本的な流れは次の通りです。

  1. flutter/flutterをGitHub上でforkする
  2. forkしたリポジトリを開発環境にcloneする
  3. cloneしたリポジトリを修正する
  4. Flutter SDKのパスをcloneしたリポジトリに設定する
  5. python3depot_toolsをPATHに追加する
    • 筆者は brew install python3 でpython3をインストールしました
  6. flutter/flutterのrootでflutter update-packagesを実行する
  7. コードの変更や動作確認を行う
  8. 修正内容をforkしたリポジトリにpushする
  9. 修正内容をPRにして提出する

ポイントはcloneしたリポジトリをFlutter SDKのパスに設定する点です。

flutter runflutter testで実行されるSDKが、コードを変更したものになるため、任意のアプリやテストを実行できます。
もしもグローバルな環境をいじりたくない場合には、fvmが利用できます。都度git pushを行う必要がありますが、PRの対応に長い時間を要しそうな場合には検討の余地があるかもしれません。[3]


~/packages/flutter に開発用のリポジトリをクローンしたとします。すると、次のような環境変数の設定をすればOKです。通常の開発で利用するFlutter SDKのパスは、一時的にコメントアウトしています。

# flutter

# export PATH=$PATH:$HOME/flutter/bin
export PATH=$PATH:$HOME/.pub-cache/bin

# flutter dev

export PATH=$PATH:$HOME/depot_tools
export PATH=$PATH:$HOME/packages/flutter/bin

これで、任意のアプリをflutter runすれば、~/packages/flutterにcloneしたリポジトリが利用されます。簡単ですね。

Issueの探し方

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

flutter/flutterには、多くのIssueが登録されています。flutter/packagesに関連づくIssueも登録されているため、最初は適切なIssueを探すのが難しいかもしれません。
貴重な時間を割くものでもあるので、自分のスキルや興味に合ったIssueを探すことは大切です。

Issueに関連づけられているlabelを見ることで、ある程度の絞り込みができます。初めてのIssueを探す場合には、次のようなlabelが付与されたIssueを探すと良いでしょう。

ただgood first issueは初心者向けのIssueとされていますが、「本当に初心者向け〜?」と感じるものも混じっています。一方、has reproducible stepsは再現手順が記載されているため、Issueの内容を手元で確認しやすくなっています。どうしてもIssueによって難度がバラつくので、has reproducible stepsが付与されている、関心が高かったり理解度が高い領域のものを確認するのが良いと思います。

もしも腕に覚えがある場合には、重要度の高いIssueを探すのが良いでしょう。flutter/flutterのIssueには、開発チームが重要度を付与したものがあります。

  • P0
    • Critical issues such as a build break or regression
  • P1
    • High-priority issues at the top of the work list
  • P2
    • Important issues not at the top of the work list
  • P3
    • Issues that are less important to the Flutter project

P0P1は対応が進んでいることが多いのですが、P2P3になると(手が回らないのか)対応が遅れているIssueが見つかります。例えば、記事の前半で紹介したPRに関連づけられたIssueにはP2のラベルが付与されていました。

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

重要なIssueを修正できれば、それだけFlutterの安定性に寄与できます。達成感もありますし、PRをマージされたときの喜びも大きいものがあります。一度P2P3のついた、自分の関心が高いWidgetや機能に関連づけられたIssueを探してみるのはいかがでしょうか? Pは難度ではなく重要度の指標であるため、(未解決なため)簡単ではないでしょうが、しかし取り組めないほど難しいものだけではないはずです。

engine

https://github.com/flutter/flutter/tree/master/engine

flutter/flutterに比べると、格段に難しいのがengineです。Flutterのコア部分である描画処理などに加えて、AndroidやiOSのネイティブAPIをFlutterで利用するための実装が含まれます。このため、プラットフォーム固有の不具合を調査していると、engineをいじる必要が出てくることがあります。

2024年12月以降、flutter/flutterflutter/engineが統合されました。このため、flutter/flutterにてengine開発用のステップを実行すると、旧flutter/engineの開発環境が構築できます。

engineの開発のためには、いくつかの不思議なステップを踏む必要があります。flutter/flutterに比べると複雑なことをしなければならないため、少々難度が高いかもしれません。
この記事では、筆者がmacOS上で環境構築した際の知見をもとに、そこそこお手軽にengineの開発環境を整えることを目指します。

なお、ビルドには時間がかかるため、余裕を持って作業することをお勧めします。もしくは、いいマシンに更新しましょう。[4]

ビルドの前に

engineの開発において、重要なのはgclientexport FLUTTER_ENGINEです。

gclientdepot_toolsに含まれているツールで、engineの依存関係を解決するために利用します。依存関係の整理の際、どのリポジトリを取得するか記述されるのが.gclientファイルです。

https://github.com/flutter/flutter/blob/master/engine/README.md

この記事を読まれる方はGooglerではないと思われるので、standard.gclient.gclientにリネームしつつflutter/flutterのrootに配置します。そしてディレクトリを移動せず、gclient syncを実行します。
これで、各種依存関係が解決され、engineの開発環境が整います。

続いて、FLUTTER_ENGINEについて。環境変数にFLUTTER_ENGINEが登録されていると、flutter run時に「開発中のlocal engineやweb sdkを指定してね」というログが出力されます。逆に言うと、FLUTTER_ENGINEがセットされていなければ、ローカルでビルドしたengineは利用できません。

この『環境変数にFLUTTER_ENGINEが登録されていると』と言う条件は厄介です。タイミングによってはFLUTTER_ENGINEを設定していても、ビルドしたengineが反映されません。そんな時にはunset FLUTTER_ENGINEや再起動で対応しましょう。

engineのビルド

monorepo化したことにより、engineのビルドに関する公式ドキュメントが整備されました。この記事を参考にしつつも、常に最新のドキュメントを参考にすることをお勧めします。

https://github.com/flutter/flutter/blob/master/engine/src/flutter/docs/contributing/Compiling-the-engine.md

もしかすると筆者のセットアップが悪いのかもしれないのですが、ドキュメント通りにコマンドを打ったとしてもninjaが見つからないと言われることがあります。その場合には、brew install ninjaを実行してください。


先ほど紹介した通り、筆者は~/packages/flutterflutter/flutterをcloneしています。このためFLUTTER_ENGINE$HOME/packages/flutter/engine/srcです。

AndroidやiOSの場合、ビルドは$FLUTTER_ENGINEで行います。と言うのも、$FLUTTER_ENGINE/outに成果物が出力されるためです。どうやらコマンドの一部箇所で相対パスを利用しているらしく、コマンドの実行場所によってビルドが成功するか失敗するかが変わるようです。
なお、Webの場合にはfeltを利用します。feltは(どうやら)$FLUTTER_ENGINEからパスをセットするらしく、どこからでも実行できるようです。大変便利なので、export PATH=$PATH:$FLUTTER_ENGINE/flutter/lib/web_ui/devをセットしましょう。

なお、ビルド時にはengineが対応するCPUアーキテクチャ等を指定します。このオプションは、公式ドキュメントを見ながら設定してください。筆者は次のようなaliasを設定していますが、どれもエミュレーター/シミュレーターで動作させることを想定しています。

alias build:ios='./flutter/tools/gn --ios --simulator --unoptimized --simulator-cpu=arm64; ./flutter/tools/gn --unoptimized --mac-cpu arm64; ninja -C out/ios_debug_sim_unopt_arm64 && ninja -C out/host_debug_unopt_arm64;'
alias build:android='./flutter/tools/gn --android --android-cpu arm64 --unoptimized; ninja -C out/android_debug_unopt_arm64 && ninja -C out/host_debug_unopt_arm64;'

またAndroidのビルドにはANDROID_SDK_ROOTを書き換える必要があります。筆者は次のように記述し、コメントアウトを切り替えることで対応しています。

#  export ANDROID_HOME=~/Library/Android/sdk
export ANDROID_HOME=$HOME/packages/flutter/engine/src/flutter/third_party/android_tools/sdk
export ANDROID_SDK_ROOT=$ANDROID_HOME

Webのビルドを行う場合には、先述の.gclientファイルに変更が必要です。

https://github.com/flutter/flutter/blob/master/engine/scripts/standard.gclient

コメントにある通りcustom_varsの設定を有効にして、gclient syncを実行します。この設定によりEmscripten SDK(emsdk)が取得され、CanvasKitのビルドができるようになるそうです。 [5]

なおfeltのオプションに関しては、あまり気にしなくても良いはずです。最初は時間がかかるかもしれませんが、felt buildを実行しましょう。細かくオプションをセットすることで、ビルド時間を短縮できるはずですが、ビルドキャッシュが存在すればそこまでの差にはならないはずです。

Issueの探し方

flutter/engineに関連するIssueを探そうと思って探す人はいないと思いますが、探したい人はengineを確認すると良いでしょう。
思い通りのIssueが見つからない場合には、team-enginetriaged-engineが付与されたIssueを探す手もあります。

大抵の場合、気がついたらflutter/engine側の調査や修正が必要になっていた、というケースでしょう。例えば、AndroidやiOSのキーボードと連携する機能の不具合を調査していると、EditTextUITextFieldに到達することがあります。このように、Issueが実はflutter/engineに関連していることもあるため、engineが付与されていなくともflutter/engineの調査や変更が必要になるケースがあります。

おまけ

これまでに紹介した環境変数をまとめて記述しておきます。

筆者はpackages/flutterengineの開発する際に、必要なコメントアウトを切り替えて運用しています。より良い運用方法をご存知の方がいれば、ぜひコメントいただきたいです!

# android

function set-studio-jdk() {
 export JAVA_HOME=$1/Contents/jbr/Contents/Home
}

set-studio-jdk "/Applications/Android Studio.app"

export ANDROID_HOME=~/Library/Android/sdk
export ANDROID_SDK_ROOT=$ANDROID_HOME

# flutter

export PATH=$PATH:$HOME/flutter/bin
export PATH=$PATH:$HOME/.pub-cache/bin

# flutter dev

# export PATH=$PATH:$HOME/depot_tools

# export PATH=$PATH:$HOME/packages/flutter/bin
# export FLUTTER_ENGINE=$HOME/packages/flutter/engine/src
# export ANDROID_HOME=$FLUTTER_ENGINE/flutter/third_party/android_tools/sdk
# export PATH=$PATH:$FLUTTER_ENGINE/flutter/lib/web_ui/dev

# alias build:ios='./flutter/tools/gn --ios --simulator --unoptimized --simulator-cpu=arm64; ./flutter/tools/gn --unoptimized --mac-cpu arm64; ninja -C out/ios_debug_sim_unopt_arm64 && ninja -C out/host_debug_unopt_arm64;'
# alias build:android='./flutter/tools/gn --android --android-cpu arm64 --unoptimized; ninja -C out/android_debug_unopt_arm64 && ninja -C out/host_debug_unopt_arm64;'

# alias run:ios='flutter run --local-engine=ios_debug_sim_unopt_arm64  --local-engine-host=host_debug_unopt_arm64'
# alias run:android='flutter run --local-engine=android_debug_unopt_arm64 --local-engine-host=host_debug_unopt_arm64'
# alias run:web='flutter run --local-web-sdk=wasm_release -d chrome'
# alias run:web:wasm='flutter run --local-web-sdk=wasm_release -d chrome --wasm'

余談

flutter/flutterにマージされたPRを通知するX(旧Twitter)アカウントがあります。

https://x.com/FlutterMerge

筆者はこのアカウントをフォローしており、PRがマージされたときに通知を受け取るようにしています。マージされたPRを眺めていると、「次のバージョンにはこんな変更が入るのか」ということに気づいたり、「あの機能、ついに実装されたのか」ということに気づいたりします。また、「え、あの機能バグってたの!?」と埋もれていたバグに気づくこともあるのではと。

flutter/flutterのIssueを解決することに直接役立つわけではありませんが、コントリビューション欲を高めることができるため、フォローすることをお勧めします。

まとめ

FlutterはOSSであり、広くコントリビューションを受け入れています。この記事ではPRを中心に解説しましたが、見つけた不具合をIssueにすることもコントリビューションの一環です。
台所の包丁を研ぐような感覚で、気軽にコントリビューションしてみるのはどうでしょうか? あなたのコードが、明日のFlutterコミュニティを支えるかもしれません。

脚注
  1. 実装の難度で言えばflutter/packagesの方が低い傾向にあります。しかしメンテナー不足が影響しているのか、flutter/flutterよりも大変な印象が筆者にはあります。 ↩︎

  2. ~/flutterの中のコードを直接編集するだけで、変更を試すことができます。アプリ開発とflutter/flutterへのPRを同時に進めるには不適切なため、本格的にコントリビューションを行うためには環境を構築するべきでしょう。さっと動作チェックする分には便利なので、筆者はやりがちです。 ↩︎

  3. 筆者が調べたところ、fvmがローカルマシン内のpathを参照する仕組みはありませんでした。もしもやり方を知っている人がいれば、コメントなどで教えてください! ↩︎

  4. 筆者はM1 ProのMBPを利用していますが、初回ビルドには30分以上かかりました。 ↩︎

  5. https://github.com/flutter/flutter/blob/master/engine/src/flutter/lib/web_ui/README.md#building-canvaskit ↩︎

GitHubで編集を提案
1

Discussion

ログインするとコメントできます