GWでたくさん寝て頭がスッキリしたので、久々にブログで。
個人の感想です
LogicとViewの共通化
Flutter allows you to build apps for mobile, web, desktop, and embedded devices — all from a single codebase.
マルチプラットフォーム向けのフレームワークを使う場合、そのフレームワークは複数のプラットフォーム向けのコードを共通化します。 Flutterの特徴の一つは、共通化する対象がLogicとViewの両方である点です。
Logicの共通化は、FlutterのアプリケーションをDartで記述できる点で達成されます。
Flutterのアプリケーションは、後述するプラットフォーム依存の処理を除けば、Dartのパッケージとコードのみで実装できます。
KotlinやSwiftで記述できるようなロジックであれば、Dartで記述することに大きな問題はないはずです。もちろん、KotlinのcollectionライブラリやSwiftのstruct
の扱いやすさなどに比べると、不恰好になる……と言う問題はあります。その一方で、async/awaitを扱うにあたり意識しなければならないアレコレが少ない点は、それらの問題を補ってくれます。
また、Viewの共通化はFlutterの描画処理によって達成されます。 2024年5月上旬現在は、SkiaとImpellerが混在している状態となりややこしいのですが、どちらにせよFlutterはネイティブのViewライブラリを直接利用しません。 このため、(Skiaであれば)Skiaが保証する範囲で、AndroidとiOSの描画が共通化されます。 つまり、Flutterのコードを書くことで、アプリケーションケーション内のViewの動作を全てコントロールできます。
Flutterのドキュメント(と実装)には、一部の処理をプラットフォームごとに切り替わる話が登場します。
振る舞いの違いは、一部のプロパティをプラットフォームごとに設定することで、実現されています。 このため、設定値を統一すれば、プラットフォームを跨いで同一の振る舞いも可能です。*1
実際にアプリケーションを動かしていると、この辺りが同一かどうかで気になってくるかもしれません。 ただ、これはアプリケーションの振る舞いがどうあるべきだと考えているかによるものなので、ここでは主題につながらないと考えています。
大前提として、Flutterで作るアプリケーションはプラットフォームごとに作る必要のないアプリケーションであるべきです。
Flutterで作るアプリケーションは、Native SDKで作るアプリケーションに比べて、ものによっては些細ではありますが
などの課題を抱えることになります。もしかすると、全てのOSで提供されない機能は、自前で実装しなければならないかもしれません。 こういった課題がクリティカルな問題となる場合には、Flutterでアプリケーションを開発するのではなく、Native SDKを利用して開発するべきだと(筆者は)考えています。
上記の問題を抱えないアプリケーションにおいては、LogicとViewをある種抽象的に記述できるFlutterは、有力な選択肢です。
Native SDK APIへのアクセス
通常、アプリケーション開発で(ほぼ)必須となるAPIは、パッケージ経由でアクセスすることになります。
多くの開発者が利用したくなるAPIは、ほとんどflutter.dev
かfluttercommunity
がメンテナンスしています。一部のパッケージがflutterからコミュニティにメンテなーが変わったという事情もあります。
代表的なパッケージを列挙してみました。 AndroidやiOSのNative SDKを利用したことがあれば、READMEに記載されているAPIに見覚えがあるはずです。
前述の通り、FlutterはViewの実装にNative SDK APIを利用しません。 このため、AndroidやiOSのViewに関するAPIへのアクセスは、ほとんどのケースで不要となります。
筆者の理解では、AndroidのSharedPreferences
やiOSのUserDefaults
、File System API、端末内でスキーマを利用した他アプリケーションの起動などはflutter.devによりメンテナンスされています。機能がプラットフォームに依存するだけテストは厄介になりますが、リポジトリを見る限り、地道に実装と検証がなされています。
ちなみに、flutter.devが管理しているパッケージにおいては、それぞれのパッケージが依存しているパッケージに対する精査も行われています。 しっかりパッケージに関するポリシーが策定され、運用されている印象です。
Flutterで開発するべきでないアプリケーションの場合には、上記のパッケージでは不十分かもしれません。 ただ一部のケースを除けば、Native SDK APIへのアクセスは上記のパッケージで十分実現できます。一例として、FlutterKaigi 2023のconference-appを挙げておきます。
なお、PDFをアプリケーション内で表示する必要がある場合などでは、公開されているパッケージを選択するか自前で実装するかを検討することになります。 頻出のパターンであれば、ユースケースに合致するパッケージが見つかるはずです。 そうでないケースでは見つからないことになりますが、それはKotlinやSwiftでコードを書いている場合と同じであって、必要なだけのコードを開発者が書くだけではないかと思っています。
描画パフォーマンス
描画パフォーマンスを考えるにあたり、最も厄介なのは、iOSのProMotionです。 fpsが固定ではなく可変となっているため、Flutterの描画パフォーマンスも可変である必要があります。
Flutterはv3.0.0以降、iOSのProMotionに対応しています。 筆者の知る限り、PlatformViewを使うようなケースを除けば*2、Flutterのコードを書けば自動的にProMotionがなされます。
公平を期すために書いておくと、Skiaを採用している場合、次の問題にぶつかる恐れがあります。
この問題を回避するには、SkiaではなくImpellerを利用することが推奨されています。 Flutter 3.10.0より、iOSではImpellerがデフォルトで設定されます。気づかないうちにImpllerを利用している、そんなプロジェクトも多いかもしれません。
Skiaに比べると、Impllerは(新規に開発したこともあり)不具合が多かったように思います。 筆者としては、アプリケーションの正常系で目立った不具合にぶつかることもなくなったので心配はしていないのですが、気になる方はGitHub Issueをチェックしておくと良いかもしれません。
画面遷移の選択肢
Androidのapp-linksやiOSのuniversal linkを考慮しない場合、AndroidやiOSのアプリケーションは起動時に開く画面が決まっており、その後はユーザーの操作によって画面遷移がなされます。
アプリケーションをシンプルに実装した場合、手続き的な画面遷移がマッチします。 手続き的というのは、Androidのtaskとstackで解説されているような、画面遷移の積み重ねがそのまま表示されている画面に反映される仕組みのことをイメージしています。
FlutterはNavigatorを提供することで、この手続き的な画面遷移を実現できます。 小規模であったり、Firebase Dynamic Linksが不要なアプリケーションであれば、手続き的な処理のみでアプリケーションが実現できます。 それ以外のケースであっても、Dialogを表示した後に画面遷移をするケースなどを思い浮かべると、手続き的な処理が必要なケースが思い浮かぶはずです。
また、Flutterの公式ドキュメントにもある通り、Deep linkingに対応するには、いくつかの方法があります。 大きく分けるとNavigator APIを使って工夫する(手続き的なAPIで工夫をする)ものと、Router APIを使う(宣言的なAPIで工夫する)ものの2つに分けられるはずです。
AndroidとiOS向けのアプリケーションでは必須とはなりませんが、Web向けのアプリケーションを開発する場合には、Routerが必要です。 Router APIを直接扱うのは難しいので、go_routerのようなライブラリを利用する必要がありますが、すでに実用できる状態になっています。
モバイルアプリケーションとウェブアプリケーションでも、どちらに寄せた画面遷移の実装も選択できます。 どのプラットフォームを重視するか、対応するかによって、設計を変えられるのがFlutterの良いところだと思います。
CI/CDのセットアップ容易性
Flutterを利用してAndroidやiOS、Webアプリケーションをビルドするのは非常に容易です。
ドキュメントを見ると、非常に長く複雑に見えます。 ただ、これらはFlutterを使わないケースでも必要になる処理であり、Flutterを利用したことで追加するべき対応はほぼありません。*3
一度設定をすれば、あとは flutter build
コマンドを実行することで、aabやipaファイルを作成することができます。
近年ではGitHub ActionやCircleCIなどFlutter SDKを利用しやすくなっており、CI/CDを整備しやすくなっています。