SODA Engineering Blog
🧑🏻‍💻

FlutterKaigi Day2レポート

2024/11/25に公開

はじめに

SODAのアプリエンジニアのみやジックです。
11月21日、22日に開催されたFlutterKaigiに参加してきたので、レポートと感想を書いていきます。
私は仕事の都合上2日目だけの参加だったので、2日目だけのレポートになりますがご承知おきください。

FlutterKaigiって?

https://2024.flutterkaigi.jp/

FlutterKaigiは日本最大規模のFlutterに関するカンファレンスです。
今年は2つのスポンサーブースと2つのセッションルームがあり、2日間の来場者は347名にも達したそうです。すごい👀
2日目はセッションの時間が7枠あり、セッションルームが2つだったので、14セッションあったことになります。
本記事では自分が聴講できたセッションの軽いまとめや感想を書きたいと思います。

自分が聴講できたセッションは下記の7つです。

  • おさたくさん
    FlutterアプリにおけるSLI/SLOを用いたユーザ体験の可視化と計測基盤構築
  • ちゅーやんさん
    体験!マクロ時代のFlutterアプリ開発
  • ronnnnnさん
    OS標準のデザインシステムを超えて
  • chigichan24さん
    Flutterと難読化
  • Dreamwalkerさん
    Seamless Flutter Native Integration: FFI & Pigeon
  • Kosuke Saigusaさん
    マッチングアプリ「Omiai」のFlutterへのリプレイスの挑戦
  • Aoi Umigishiさん
    SliverAppBarはなぜ変化する? ~ Sliverを内側から理解する ~

FlutterアプリにおけるSLI/SLOを用いたユーザ体験の可視化と計測基盤構築

https://speakerdeck.com/ostk0069/slowoyong-itayuzati-yan-noke-shi-hua-toji-ce-ji-pan-gou-zhu
https://www.youtube.com/live/CE0NTGJLbS8?si=6L9LcbZK3mJyOWJF&t=2682

スライドを開いていただけるとわかるのですが、この発表資料は145Pもあるそうです。すごい、、

SLI/SLOとは

SLI(ServieLevelIndicator):定量的に測定可能なユーザの満足度の指標
SLO(ServiceLevelObjectives):SLIが達成すべき目標

一般にSLI/SLOはAPIリクエスト単位で計測しているケースが多いですが、それをユーザ体験ベースで計測するためにクライアントアプリから計測するというアプローチのお話でした。
SLOの決定にはCUJ(クリティカルユーザジャニー)を考えることで、ユーザ体験ベースでのSLI/SLOを計測しているとのことでした。

https://speakerdeck.com/ostk0069/slowoyong-itayuzati-yan-noke-shi-hua-toji-ce-ji-pan-gou-zhu?slide=27

Winticketの例であればログインから購入までがCUJと言えるとのことでしたが、スニダンで考えるとログインから購入までのフローやログインから出品までのフローなどをSLI/SLOの計測対象としても良さそうだなと思いながら聴いてました。

計測基盤について

計測基盤はSentryを組み込んでエラーのアラートと一緒にSLIのアラートも扱えるように開発したそうです。
また、計測したSLIについてはダッシュボードから見れるようにしているようで、エンジニアじゃなくてもこのダッシュボードであれば「ん?なんかおかしいぞ?」と気づくことができるようになっているとのことでした。

確かにこれなら誰が見ても「ん??」って思えそうですね!

また、計測のクライアントアプリのサンプルは下記で公開されているそうです。

https://github.com/ostk0069/sli-slo-flutter-example

結果

この取り組みによる改善は下記のような結果のようになったそうです。
表を見ると障害をしっかりと検知できるようになってて素晴らしいですね。

体験!マクロ時代のFlutterアプリ開発

https://www.youtube.com/live/CE0NTGJLbS8?si=Qd8CSaKtOHO5xZOs&t=5507
https://github.com/chooyan-eng/macros_practice/tree/chore/flutter_kaigi_2024

マクロを使ったデモ

ちゅーやんさんの発表ではSharedPreferencesやNavigationを題材にマクロを使ったライブコーディングをしていただきました。
SharedPreferencesの例ではマクロを使うことで、keyのスペルミスを防いだり型安全に実装する小細工独自実装を排除して型とkeyを定義するだけで、必要なメソッドが全て生成されるdemoがマクロの良さがとてもわかりやすかったです。
ボイラープレートのコードは生成AIの発展によって多少めんどくささが解消されているものの、コードの読解の負担は減るわけではないので、ボイラープレートコードが減っていくのはやっぱり嬉しいですね。

また、build runnerを都度実行し直さなくても良いのもマクロの良さだと思うので、マクロがFlutterのStableVersionになって、色々なパッケージが対応されるのが楽しみですね。

マクロを作るデモ

マクロはdartで書くプラグラムなので、コード生成用のコードの多少の癖やテクニックなどはあるにしても基本的にはdartが書ける我々なら書けるというのがとても良いと思いました。

マクロはまだStableではないためFlutterをmasterチャンネルの最新に変更する必要があります。
実装は下記の順でできるそうです。
① macro用のクラスを定義する
② macroキーワードを先頭につける
③ Macrosインターフェースを実装する
④ const コンストラクタを追加する
⑤ 生成メソッドをオーバーライドする
⑥ なんやかんやあって出来上がり

なんやかんやってなんだ?といった感じですが、ここはコードを生成するためのロジックを書くということなのかなと思います。

これ以降の話は実際にマクロを書いてみないと理解できなさそうだったので割愛します。

全体の感想としてはマクロのコード生成の速さがすごいというところと、Flutterの未来がより楽しみになったという感じでした!

OS標準のデザインシステムを超えて

https://www.youtube.com/live/CE0NTGJLbS8?si=Qd8CSaKtOHO5xZOs&t=5507
https://speakerdeck.com/ronnnnn/os-biao-zhun-nodezainsisutemuwochao-ete-yorirou-ruan-na-flutter-temaguan-li-flutterkaigi-2024

言葉の認識合わせ

テーマ周りの言葉はコンテキストによって意味が変わってくるとのことで、言葉の定義から発表を始められてました。発表に限らずMTGなどでもキーワードの意味の認識がずれているとミスコミュニケーションが発生するので、言葉の定義を確認して認識をそろえるのはとても大事ですね。
今回の発表の中では下記の3つの言葉の定義の認識合わせははじめにしていただいていました。

コンポーネント:インタラクションを提供するWidget
スタイル:コンポーネントに与える視覚的な情報
テーマ:スタイルを共通の関心ごとでグルーピング

言葉の認識合わせ大事!

テーマを用いたスタイル反映のアプローチ

Flutterにおけるスタイル反映の優先度は下記のようになっているそうです。みなさん知ってましたか?自分はぼんやり理解していたようなしていなかったような感じです。。。
狭いスコープで指定されたスタイルがより優先されるってことですね。わかりやすい!

全部Widgetに反映すればあまり何も考えなくても好きなWidgetに好きなスタイルを適用できますが、そうするとデザインの適用を忘れたり、画面ごとに同じ用途のボタンなのに違うスタイルが当たってしまったり、スタイルを変更する際に一括で変更できなかったりのデメリットがあるので、テーマを利用することでスタイルをより楽に反映するアプローチを3つ紹介していただきました。

  • ThemeDataを利用する
  • InheritedWidget(や他の状態管理パッケージ)を利用する
  • ThemeExtensionを利用する

ここでは詳しい中身については触れませんが、これらを組み合わせながら自分のアプリのデザインの要件にあった戦略をとっていくのが大事とのことでした。銀の弾丸はない。

また、ThemeExtensionではサブクラスの実装が冗長なため自動生成のパッケージがいくつかあるようです。

発表で紹介されていたパッケージはこちら

https://pub.dev/packages/theme_tailor

ですが、ronnnnnさんこれをマクロで実現する試みもしているみたいで、パッケージも公開しているようです。すごい。

https://pub.dev/packages/thema

ちゅーやんさんの発表も相まって大マクロ時代が到来するなぁと感じました。

テーマ周りはいつも苦手意識を持っていましたが、改めて向き合う良いきっかけとなる素晴らしい発表でした!

Flutterと難読化

https://www.youtube.com/live/zqDJPyHSFV8?si=pjytSNBjfFFCqOVY&t=17305
https://speakerdeck.com/chigichan24/fluttertonan-du-hua

↑のコードはFlutterKaigiアプリのとある画面のコードが難読化されたものだそうです。
発表の冒頭でアイスブレークとして実際に難読化されたコードを見せてくれることで難しそうな難読化の話について一気に興味が湧いてきました🔥

発表でおっしゃられていた中で最も重要なのは下記の2点かなと思います。

  • 難読化は暗号化ではない
  • 難読化はリバースエンジニアリングを完全に防ぐことができるわけではない、一定の効果がある程度

また、FlutterにおいてはReleaseBuildの場合、デフォルトで難読化されるそうです。よくFirebaseのCrashlyticsにシンボルファイルをアップロードしてくださいという警告があると思いますが、あれはFlutterのアプリがデフォルトで難読化されているからだったんですね。

難読化の仕組み

難読化は純粋にソースコード内のシンボル(class名や変数名など)をランダムな文字列に置き換えることでソースコードを"人間"が読みにくい形に置き換える処理のことだそうです。
先ほど述べたシンボルファイルとはこの置き換えた時の置き換え対応表のことで、ビルド時にFlutterコマンドにoptionをつけることで出力できるそうです。

また、FlutterではDartVMがJITとAOT2つのmodeを提供していて、AOTでのみ難読化を行っているそうです。JITはデバッグ時に使われるmodeなので、確かにデバッグ中にスタックトレースが難読化されてたら不便で仕方ないですもんね!
難読化はシンボルをランダムな文字列に置き換えると言いましたが、基本的にはアルファベットのa~z、A~Zを順番に割り当てていっているだけみたいです。
そんなシンプルでいいのかなとも思いましたが、そもそも暗号化ではなく読みにくくするのが目的の以上これ以上複雑にする必要もなんだろうと思います。

難読化って言われると一見むずかしそうに聞こえますが、説明してもらえるとなんだ簡単なことだね!ってなりました。

Seamless Flutter Native Integration: FFI & Pigeon

https://www.youtube.com/live/CE0NTGJLbS8?si=J7F4zbAV1pNav1FF&t=21698
https://speakerdeck.com/itsmedreamwalker/bagjecang-at-flutterkaigi2024

DreamWalkerさんは韓国の方で、今回FlutterKaigiのために来日してくれたそうです。
また、発表自体は英語でされていましたが、日本語も話されるので登壇後に日本語で質問させてもらったりもしました。日本語話せるのすごい。

Pigeon

LLMの文脈でTensorFlowやMLXなどネイティブレイヤー向けのSDKやAPIが公開されている機能をFlutterでも使うためにはMethodチャンネルを使う必要があります。
また、デバイス固有のセンサーの値などを継続的に関しする場合はEventチャンネルを使う必要があります。

これらのチャンネルでのやり取りは文字列で名前解決が必要かつTypeSafeに扱えないというデメリットがあります。
Pigeonを使うことで、名前解決や型解決を自動生成コードに任せることができ、安全なコードを書くことができるそうです。

FFI/FFIGen

FFIとはForeign function interfaceの略であるプログラミング言語から他のプログラミング言語の処理を呼び出すための機構だそうです。
特にFlutterに限った機構ではなく、広くプログラミングの世界で使われているインターフェースですが、FFIで処理を呼び出すためにはLookup Functionという処理を呼び出すための窓口を呼び出したいメソッドごとに定義する必要があるそうです。それが少し手間だとか ☺️
そこで、FFIGenはLookupFunctionのコードを自動生成してくれてメソッド1つずつを我々が手書きしなくてもすぐに呼び出せるようになるそうです。ボイラープレートを自動で書いてくれるのはやっぱり嬉しいですね!

マッチングアプリOmiaiのFlutterへのリプレイスの挑戦

https://www.youtube.com/live/zqDJPyHSFV8?si=H7dRxQqmiJj1iEA3&t=24500
https://www.slideshare.net/slideshow/omiai-flutter-flutterkaigi-2024/273516514

マッチングアプリOmiaiでは10年運用してきたアプリのFlutterへのリプレイスを機能開発を止めることなく行うという挑戦をしているとのことでした。
Flutterへのリプレイスは別アプリとして新たに開発するのではなくAdd-to-Appを用いて、特定の機能や画面から少しずつFlutter化をしていくとのことで、最初はネイティブアプリからFlutterアプリを呼び出す形で実装していき、Flutterアプリ領域を増やしていき、どこかのタイミングでFlutterアプリからネイティブアプリを呼び出す形に切り替え、最終的には全てFlutter化するという道のりだそうです。

陥りがちな現実

発表ではFlutterでのアプリ開発でよく見る技術負債をいくつか紹介されていたので、列挙してみます。

  • UI層から直接Repository層のメソッドを呼ばないという規約を作ったけれど、実際にはPRで気づく以外に防ぐ方法がない。
  • UIとロジックの分離の失敗。ビジネスロジック層がBuildContextに依存してしまう。
  • 不自然な依存。UI層が特定のHTTPクライアントパッケージに依存するのは不自然。
  • 曖昧な例外ハンドリングの方針。
  • 依存を誤って負債化。通信層が業務知識としての認証情報に依存する。
  • クライアント・サーバサイドアプリの密結合

それぞれの詳しい内容はスライドや発表動画を見ていただきたいのですが、自分も見たことがあったり書いてしまったことがあったり耳の痛い内容でした。

負債を防ぐ仕組み

発表では負債を防ぐ仕組みとして3つの取り組みが紹介されていました。

  • マルチパッケージ構成
  • ユニットテストを十分に書く
  • 実装を可能な限りパターン化する

マルチパッケージ構成は、アーキテクチャーのレイヤーごとにパッケージを分けることで、依存関係のルールを仕組みで防いでいるそうです。特に印象的だったのはFlutterエンジニアとしてコードを書く時とdartエンジニアとしてコードを書くときの意識を分けているそうです。また、アーキテクチャについては下記の記事でも詳しく書かれていたのでぜひ参考にしてみてください。
https://zenn.dev/omiai_techblog/articles/omiai-flutter-architecture

業務ロジックについてはカバレッジ100%でユニットテストを書いているそうです。マルチパッケージ化をすることで、各レイヤーの責務が明確・単一化されることでユニットテストが書きやすくなるというメリットもあるそうです。綺麗なコードだとテスト書きやすいですもんね。

また、public_member_api_docsというLintルールを有効化することで、すべてのパブリックなメンバーについてdoc commentを記述しているそうです。

SliverAppBarはなぜ変化する? ~ Sliverを内側から理解する ~

https://www.youtube.com/live/CE0NTGJLbS8?t=28900s
https://speakerdeck.com/umigishiaoi/sliverappbarhanazebian-hua-suru-sliverwonei-ce-karali-jie-suru

SliverAppBarを使ったことはありますでしょうか?スクロールに応じて見た目が変わるようなAppBarをFlutterで実現しようとすると出会うのがSliverだと思います。本発表ではなぜSliverではそれが実現できるのかを紹介してくれていました。

RenderBoxプロトコルとRenderSliverプロトコル

Flutterの描画システムには下記の3つのツリーが存在します。


Widgets→普遍の設定値
Elements→ 実態を管理するもの
RenderObjects→ 実態を描画するもの

本発表では3つ目のRenderObjectsが持つperformLayoutメソッドについて、RenderBoxサブクラスとRenderSliverサブクラスのそれぞれでの実装について詳しく紹介してくれていました。

自分は「SingleChildScrollViewを使うと画面に表示されない部分まで描画計算がされちゃうけど、ListViewやCustomScrollViewを使うと画面に表示される部分だけ描画計算がされるので、パフォーマンスが良くなる」程度の理解でしたが、なぜそうなるのかを描画計算のプロトコルレベルでわかってとてもスッキリした気分になりました。内部実装まで読んで理解するのは労力のかかることなので、とてもありがたいです。

描画計算のプロトコルがどう違うか気になる方はぜひ発表動画や発表資料を見てみてください!

おわりに

今回レポートを書けたのは2日目の発表の半分だけなので、他の発表についてはぜひYouTubeのアーカイブからご覧ください!

https://www.youtube.com/@flutterkaigi/featured

※ 本記事で利用したスクリーンショットはすべて登壇資料からの引用になります。

SODA Engineering Blog
SODA Engineering Blog

Discussion