綴Photo の勉強ノートに戻る
コードレビュー学習2026.05.21

『端末で完結』を謳うアプリの隠れたネットワーク依存 — Google Fonts のランタイム取得

Fluttergoogle_fontsオフラインプライバシー綴Photo

Codex と Claude のレビューで浮かんだ「オフライン依存」まわりの観察を、勉強のためにまとめたものです。

綴Photo のモバイル版は「端末ローカルで完結する手軽さ」を価値にしています。ネットワークが無くても日々綴れる、というのが売りです。ところがレビューで「起動時にフォントを Google のサーバーから取得している」という指摘が出た。「ローカル完結」と言いながら、実は初回起動でネットワークに出ていた、という観察。

この記事で扱う観察ポイント

  • google_fonts パッケージはフォントを「実行時にダウンロード」する設定になりえます
  • 「オフライン完結」を価値にするアプリで、これがどう矛盾するか
  • フォントを bundle(同梱)してネットワーク依存を切る対策

フォントが実行時にダウンロードされていた

コードで起きていたこと

綴Photo のモバイル版は、起動時にこのフラグを true にしていて、フォント本体はアプリに同梱されていなかった。

dart
void main() async {
  // ...
  // フォントを実行時に取得する設定。本体は同梱していない
  GoogleFonts.config.allowRuntimeFetching = true;
  runApp(const ProviderScope(child: TsuzuphotoApp()));
}
書体の実体は Google の CDN。アプリ本体には無い。フラグは起動時メモリ。DB には無い。アプリ本体assets/fonts書体ファイルは無い配布物。DB ではない起動時メモリallowRuntimeFetchingtrue毎起動で書く。保存しないGoogle の CDNフォントの実体 .ttf外部サーバーここから取る端末のキャッシュファイルに書く成功後に残るDB ではない成功後、実体は端末のキャッシュファイルに残る。2回目からは CDN に出ない。フラグは毎起動のメモリ。書体ファイルは本体か CDN か端末ファイル。どれも DB ではない。

動画を観るとき手元に動画ファイルが無くても配信サーバーから流れてくるのと同じで、フォントも手元に無ければ外部から取りに行きます。

  • オフライン: ネットワークが無い状態で初回起動すると、本来の書体が出ず、フォールバックのフォントで表示が揺れます。「ネット無しでも使える」という体験が、初回だけ崩れます
  • プライバシー: 「端末で完結する」と謳いながら、初回に外部(Google)へリクエストが飛びます。何も外に出ないと思っていたユーザーの期待と、実態がズレます

なぜそうなりやすいか

google_fonts パッケージが「同梱不要で手軽」を売りにしているからです。GoogleFonts.xxx() と書くだけで動いてしまうので、開発中は「フォントを同梱する」という発想自体が出てきません。開発機は常にネットに繋がっているから、ランタイム fetch していることに気づきません。「ローカル完結」というプロダクト方針と、ライブラリのデフォルト挙動が、静かにズレます。

対策の方向(と、綴Photo が選んだトレードオフ)

素直な対策は、使う書体を assets/fonts/ に同梱して、ランタイム fetch を禁止すること。

dart
// フォントを bundle 済みにして、実行時 fetch を禁止する場合
GoogleFonts.config.allowRuntimeFetching = false;
yaml
# pubspec.yaml にフォント本体を登録
flutter:
  fonts:
    - family: KleeOne
      fonts:
        - asset: assets/fonts/KleeOne-Regular.ttf

フォントのライセンス(多くは SIL Open Font License)も一緒に同梱しておきます。

ただし、これは片方が正解という話ではなくトレードオフだった。綴Photo が使っているのは日本語の3書体(KleeOne / Yomogi / ZenKurenaido)で、日本語(CJK)フォントは1書体でも数 MB〜十数 MB と大きいです。3つ bundle するとアプリサイズが +20〜35MB ほど膨らみます。

綴Photo では最終的に「アプリサイズの最小化を優先して、実行時取得を維持する」判断にした(= allowRuntimeFetching = true のまま)。「オフライン完結なのに外部取得」というレビュー指摘は何度も出るポイントなので、コードに意図をコメントで残して「これは見落としではなく選択」と明示してある。

書体の置き場所で、初回にネットへ出るかが決まる。フラグは起動時メモリ。DB には無い。同梱する場合アプリ本体assets/fonts に .ttf配布物。閉じても残る起動時メモリallowRuntimeFetching false保存しないGoogle の CDN出ない実体は本体側実行時取得(選んだ方)アプリ本体書体ファイルは無い配布物は小さい起動時メモリallowRuntimeFetching true保存しないCDN → 端末ファイル初回に外部から取る成功後はキャッシュ。DBではない

なので学びは「bundle に直すべき」ではなく、「ローカル完結」と「アプリサイズ」のどちらを取るかを、知らないまま放置せず、意識的に選んで記録しておくこと。綴Photo はサイズを取ったが、プライバシーやオフライン体験を最優先するアプリなら bundle を選ぶ場面もあります。

学びポイント

  • 「手軽に使える」ライブラリは、その手軽さの裏で外部リソースを実行時に取りに行っていることがあります
  • 「オフライン完結」「ローカルで完結」を価値にするなら、起動時・初回に外部へ出る箇所が無いかを確認します。フォント・アイコン・解析 SDK などが盲点になりやすいです
  • 開発機は常時オンラインなので、ネット依存は実機をオフラインにして初回起動しないと気づきにくいです

このテーマの括り

「ローカル完結」はプロダクトの約束です。その約束は、コード全体が守って初めて成り立ちます。便利なライブラリのデフォルトが「実行時に外部から取得」だと、約束に小さな穴が空きます。フォント1つの話に見えて、実は「自分のアプリが本当に外部に何も出していないか」を点検する練習材料になります。


おわりに

この指摘は機能バグではない。アプリは普通に動く。でも「ネット無しでも使える」「端末で完結する」という言葉を看板に掲げるなら、その言葉と実装がどこまで一致しているかは把握しておきたい。綴Photo の場合は「初回のフォント取得だけはネットに出る」というズレを把握した上で、アプリサイズとの兼ね合いで「実行時取得を維持する」と意識的に選んだ。大事なのは看板と実装を一致させること自体ではなく、ズレを知らないまま放置しないこと。それを点検するのは、レビューが拾える範囲です。


この記事は2026年5月時点の自分の理解で書いています。google_fonts パッケージの挙動はバージョンで変わることがあるので、最新の公式ドキュメントも合わせて参照してください。