【アドベントカレンダー2025】DevTools で分かる Flutter アプリの動きと最適化の考え方

こんにちは!ぐるなびアプリの開発をしている戸川です。モバイルアプリの「なんとなく重い」「ときどきカクつく」といった問題を、Flutter DevTools を使って可視化して、どのように原因を切り分けていくのかを解説していきます。

はじめに

モバイルアプリの品質を保つうえで、パフォーマンス計測は重要です。 Web と違い、Flutter では描画やレイアウト計算をアプリ側で担うため、画面構造が複雑だったり不要な処理が混在したりすると、動作が重く感じられることがあります。

そこで本稿では、Flutter DevTools を使って動作を可視化し、どのように原因を切り分けてきたかを紹介します。DevTools を使うとアプリ内部の挙動を視覚的に確認でき、どのあたりにボトルネックがあるかを把握しやすくなります。

DevTools で確認できる代表的な機能例:

  • Performance view
    • フレームごとの処理時間を確認し、UI Jank や Raster Jank の原因を特定
  • Network view
    • API コールの回数やタイミングを確認し、無駄な通信を発見
  • Memory view
    • メモリの増減やリークの兆候をチェック

本記事では、実際に DevTools を使って行った検証と、その結果わかったポイントを紹介します。

パフォーマンスの検証

まずは Performance view を使って、画面描画まわりの負荷を確認しました。

Performance view

Performance view では、アプリのパフォーマンス問題や UI の カクつき(Jank) をタイムラインと照らし合わせながら視覚的に調査することができます。

Performance view

Performance view では、タイムラインと合わせて Jank の発生箇所や長時間を要したフェーズを視覚的に調査できます。Flutter の基準は 60fps(1フレーム ≒ 16.7ms)で、これを超えると Jank が発生します。フレームは主に次の 2 フェーズに分かれます。

  • UI: Widget の組み立て・レイアウト計算
  • Raster (GPU): 実際の描画処理

どちらかのフェーズが基準値を超えると DevTools 上で警告が表示されます。

Jank の検出例

検証中、一部の画面で Jank が検出されるケースがありました

UI Jank の検出例

UI Jank Detected. Build was the longest UI phase in this frame.

これは、このフレーム内で UI スレッドが実行した処理の中で、「Build フェーズ」が最も時間を消費していたことを示しています。Build フェーズが長くなるということは、State の変化により不必要な範囲までリビルドが伝播しているか、Widget ツリーのレイアウト計算が重くなっている可能性が考えられます。

また、リッチな表現を含む別の画面では Raster 側での負荷が検出されました。

Raster Jank の検出例

Raster Jank Detected. Canvas.saveLayer() was called 16 times during this frame.

これは、1フレームの Raster(描画)処理の中で saveLayer() が 16 回も呼ばれており、そのために描画負荷が大きくなっているという警告です。saveLayer() は Flutter の描画パイプラインの中でも比較的コストが高く、以下のような表現を行う際に内部で使われることが多いAPIです。

  • ColorFilter
  • ShaderMask
  • Clip
  • 複雑な合成処理
  • 半透明の重ね合わせ

実際に検証では、半透明のオーバーレイや複数のアイコン表現が重なっているシーンでこの傾向が見られました。 演出が増えるほど saveLayer() の負荷も比例して増えるため、UI の表現とパフォーマンスのバランスを見直す必要がある、という示唆が得られました。

Rebuild Stats による観察

Performance view では Rebuild Stats という、どの Widget がどれだけリビルドされたかを確認できるページがあります。 実際に検証してみると、キーボードの開閉に関連して再構築が多く発生する状況が確認されました。キーボード開閉時には MediaQuery が更新され、これを参照する上位 Widget がリビルドされるとその影響が子孫 Widget に連鎖していきます。

Rebuild Stats / キーボード開閉時に大量のリビルドを検出

キーボードの開閉では、画面下部がせり上がる関係で MediaQuery が更新されます。 MediaQuery は画面サイズや余白(SafeArea・viewInsets など)を提供する仕組みのため、これが変わると 画面全体のレイアウトが再計算されます。

ツリー構造が深いと、親のリビルドが子へ波及して全体の再構築量が増えるため、キーボード操作を契機にフレームの遅延が出やすくなります。

補足:リビルド自体が完全に悪いわけではなく、何が適切な再構築かを見極めることが重要です。

Network view

Network view では、アプリが実行するすべての HTTP リクエストとレスポンスの詳細を視覚的に確認できます。 Chrome DevTools の Network タブに近い使い心地で、モバイルアプリの挙動をよりダイレクトに把握できるのが特徴です。

主に確認できるのは次のような情報です。

  • 各APIのコールタイミング
  • リクエスト / レスポンスボディ
  • HTTPステータス
  • Duration
  • サイズ、ヘッダ、メソッドなど

重複コールの検出例

検証の中で、ほぼ同時に同一の API が複数回コールされているケースが確認されました。 無駄な API コールは以下のような影響を与えます。

  • ローディング時間が伸びる
  • 過剰なUIの更新やカクつきが増える
  • サーバー側の負荷も増える

Network view / 重複コールの検出

このため、重複防止やコールのタイミング制御、状態管理の見直しなど実装面での改善を進めています。

また、Network view は Duration 順にソートすることもできるため、レスポンスが遅い API の洗い出しと優先度付けが簡単にできました。

まとめ

今回の検証では、Flutter DevTools を使って UI 側や Raster 側、ネットワーク面といった観点でボトルネックの候補を可視化しました。 具体的には以下のような示唆が得られました。

  • Build フェーズが長くなるケース
    • → 不要なリビルドの波及を抑える設計検討
  • saveLayer() の多用が疑われるケース
    • → 表現の簡素化や代替表現の検討
  • API の重複コール
    • → 状態管理の見直しやキャッシュ戦略を検討

DevTools を使って実際の動きをデータとして確認できると、「なんとなく重い」「ときどきカクつく」といった曖昧な症状も、根拠を持って説明・改善できるポイントとして捉えやすくなります。

Flutter DevTools には今回紹介したもの以外にも、CPU Profiler や Inspector など多くの便利な機能があります。 アプリのパフォーマンス改善を進める際は、ぜひ積極的に活用してみてください。


ぐるなびのモバイルアプリエンジニア。アクアリウムが大好き。魚・エビ・カニ・ヤドカリ・貝・カメが家にいます。