🔤

Flutter on the webでGoogle Fontsを使ってみよう

に公開

最近、自身のこれまでのキャリアの整理や、副業において自身の経歴や実績などのアピールのために、Flutter on the webを用いてポートフォリオを作成しています。

FlutterのWebアプリをデフォルトの設定で作成すると、テキストの漢字表記が一部中華系フォントのような見た目になってしまうため、なにか外部のフォントを取り入れてみようと思い、 google_fonts パッケージを導入しました。

その際、Webならではの問題に当たったこともあり、自身の備忘録も兼ねて共有します。

導入

Flutterにはデフォルトでttfやotfなどのフォントファイルをアプリに同梱する方法があります。ただし、今回の google_fonts パッケージを導入する場合、基本は追加設定は不要でGoogle Fontsで公開されているフォントを利用することができます。

https://pub.dev/packages/google_fonts

自身のアプリへの導入は、以下のコマンドを実行するだけです。

$ flutter pub add google_fonts

使い方

GoogleFonts クラス直下に様々なフォントの TextStyleTextTheme が存在しているので、Google Fontsのページを見ながら自身が作っているアプリに導入したいフォントを選びましょう。

https://fonts.google.com/

GoogleFonts メソッド候補一覧

上記のスクリーンショットを見るとわかるのですが、同じフォントで違う名称のメソッドが2つ並んでいます。

  • フォント名のみ:TextStyle を返すメソッド
  • フォント名 + textThemeTextTheme を返すメソッド

そのため、 Text() クラスなどに直接フォントを反映させる場合は前者を、アプリ全体のフォントとして ThemeData に反映させたい場合は後者のメソッドを指定するとよいでしょう。

以下のスクリーンショットは、 ThemeData クラスに「Noto Sans Japanese」を設定した前と後になります。

Google Fonts 適用前

Google Fonts 適用前

Google Fonts 適用後

Google Fonts 適用後

微妙な差でもありますが、馴染みのある日本語漢字のグリフで表示されています。

Flutter on the web特有の問題

初回ページ表示時、フォントが反映されるまで全角文字が豆腐になってしまう

これは、対応するフォントをインターネットから取得し、反映されるまでに時間がかかるからのようです。内部的な処理はわかっていませんが、それまでは全角文字が表示できないフォールバックされたASCII系のフォントが使われているのかもしれません。

その場合、フォントが利用可能となるまで待つためのメソッド GoogleFonts.pendingFonts() を使うことでこの問題を解消できます。

https://pub.dev/documentation/google_fonts/latest/google_fonts/GoogleFonts/pendingFonts.html

Returns a Future which resolves when requested fonts have finished loading and are ready to be rendered on screen.

戻り値は Future<List<void>> となっているため、 async/await もしくは FutureBuilder を使ってフォントのレンダリングが可能となるまで待つことができます。

私はローディング中にProgressIndicatorを表示したいため、 FutureBuilder を使い、フォントがレンダリング可能となるまでポートフォリオのページを表示しないようにしました。

class PortfolioApp extends StatelessWidget {
  const PortfolioApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Portfolio',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        textTheme: GoogleFonts.notoSansJpTextTheme(),  // Google Fonts内の「Noto Sans Japanese」を利用
      ),
      home: FutureBuilder(
        future: GoogleFonts.pendingFonts(), // Google Fontsの読み込み待ち
        builder: (context, asyncSnapshot) {
          if (asyncSnapshot.connectionState != ConnectionState.done) {
            // 読み込み中はProgress Indicatorを表示
            return const Center(child: CircularProgressIndicator());
          } else {
            // 読み込み完了後、ページを表示する
            return SelectionArea(child: const RootPage());
          }
        },
      ),
    );
  }
}

※グルグルのアニメーションが発生しない詳細な原因は不明ですが、バックグラウンドでフォントの読み込み処理が行われていることが起因していそうです

IntrinsicHeight + Row を使っても適切な高さにならない

先述した最初だけ全角文字が豆腐になってしまう問題の弊害がもう一つあります。ポートフォリオのページ中程まで行くとコンテンツを2列で表示している部分があり、それぞれの列にあるコンテンツの1行分の高さを揃えるために IntrinsicHeight + Row を使っている部分があります。

この部分のテキスト表示について、最初に全角文字が豆腐となってしまうことでその段階でレイアウト計算が行われて1行の高さが決定してしまい、その後にNoto Sans Japaneseのフォントに置き換わるとテキストコンテンツが1行の中に収まらなくなってしまう問題も起きていました。

overflow_text_contents

この問題についても、先ほどの GoogleFonts.pendingFonts() を用いてフォントがレンダリング可能となるまで待つことにより、解消することができます。

ページが表示されるまでの時間が長い

レイアウト上の問題は解消したのですが、今度はページが表示されるまで時間がかかってしまう問題が発生しました。

これは、デフォルトでは必要なフォントをインターネットから取得していることが原因となります。

google_fonts パッケージでは、アプリのassetsとしてフォントファイルを同梱することで、アプリ内にあるフォントファイルを利用し、最初のロード時間がグッと速くなります。

1. Google Fontsのページからフォントをダウンロード

https://fonts.google.com/

上記から利用するフォントを検索し、フォントファイルをダウンロードします。

今回は「Noto Sans Japanese」を例に進めます。

https://fonts.google.com/noto/specimen/Noto+Sans+JP

ページ右上にある「Get font」ボタンをクリックし、遷移先のページで「Download all」をクリックするとフォントファイルが格納されたzipファイルをダウンロードできます。

2. アプリのassetsとしてフォントファイルを同梱する

ダウンロードしたzipファイルを解凍すると、以下のようにファイルが格納されていました。

Noto_Sans_JP
├── NotoSansJP-VariableFont_wght.ttf
├── OFL.txt
├── README.txt
└── static
    ├── NotoSansJP-Black.ttf
    ├── NotoSansJP-Bold.ttf
    ├── NotoSansJP-ExtraBold.ttf
    ├── NotoSansJP-ExtraLight.ttf
    ├── NotoSansJP-Light.ttf
    ├── NotoSansJP-Medium.ttf
    ├── NotoSansJP-Regular.ttf
    ├── NotoSansJP-SemiBold.ttf
    └── NotoSansJP-Thin.ttf

中にはVariable FontとFontWeight毎のフォントファイルが用意されています。今回は static 配下にあるフォントを使用します。

static 配下にある NotoSansJP-*.ttf をすべてアプリプロジェクトの assets/fonts 配下へコピーし、pubspec.yaml に以下の記載を追加します。

flutter:
  assets:
    - assets/fonts/

assets/fonts というディレクトリは好きな名称にすることができます

あとはアプリを実行してみましょう。ロードがとても速くなっていることが分かります。

Flutter on the webも進化している

これまでのスクリーンショットを見てみると、テキストも選択可能状態となっており、ボタンをタップしたらちゃんと別タブでリンク先が開くなど、通常のHTMLでレンダリングされたWebページとあまり大差なく表示ができています。
もちろん、Flutter on the webが苦手な領域もありますので、適材適所だとは思いますが、SPA(Single Page Application)が向いているWebサービスの場合、プロダクションレベルでも十分に活用できるレベルではないかと感じています。私も自身のポートフォリオの作成に利用しております。

これからもどんどんFlutterを活用し、様々なサービスを開発していきたいなと思います。

参考

https://www.memory-lovers.blog/entry/2023/09/18/104233

https://zenn.dev/enoiu/articles/596078e878145d

Discussion