【Flutter Golden Test】実際のフォントを反映させる【google_fonts, material_symbols_icons】

2024/12/29 23:17公開
2025/01/03 19:18最終更新
Table of Contents
  1. 前置き
  2. Google Fontsのフォントをテスト環境に読み込む
  3. Material Symbolsのアイコンフォントをテスト環境に読み込む

前置き

Flutterには、ウィジェット等の描画結果をpngとして出力し、その差分をピクセル単位で測定することで意図した表示となっているかどうかをテストする「Golden Test」と呼ばれる手法がある。ここでは、文字を表示させた際の描画結果も当然重要になってくるが、Flutterのテスト環境ではデフォルトで真四角のフォントが使用されるため、Golden Testを行う際には不都合が生じる。

image.webp

Flutterのドキュメントを参照すると、pubspec.yamlで定義したフォントを利用している場合には、以下のコードを用いるとそのフォントをテスト環境に反映させることができると書かれている。

flutter_test_config.dart

Future<void> testExecutable(FutureOr<void> Function() testMain) async {
  setUpAll(() async {
    final Future<ByteData> font = rootBundle.load('path/to/font-file/Roboto.ttf');
    
    final FontLoader fontLoader = FontLoader('Roboto')..addFont(font);
    await fontLoader.load();
  });

  await testMain();
}

FontLoaderコンストラクタの第一引数には、フォントファミリー名(文字列) を設定する。

しかし問題になるのが、google_fontsパッケージやmaterial_symbols_iconsパッケージを利用している場合である。

GoogleFontsのTextThemeを適用した状態でGolden Testを実行してみたが、なぜかフォントのダウンロードの段階でエラーが発生し失敗した。そのため、アセットとしてフォントのファイルをダウンロードしておき、pubspec.yamlにassetsとして登録した。こうするとこちらが優先され、ダウンロードは行われなくなる(こちらを参照)。

この状態で、安直に以下のように書いて実行してみる。しかし…

// Google FontsのMurechoフォントを使用
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
  setUpAll(() async {
    final Future<ByteData> font = rootBundle.load('assets/google_fonts/Murecho/Murecho-Regular.ttf');
    
    final FontLoader fontLoader = FontLoader('Murecho')..addFont(font);
    await fontLoader.load();
  });

  await testMain();
}

image.webp
なぜか「日」だけフォントが反映されているが、大半が豆腐のまま。

さらには、Material Symbolsに至ってはフォントファミリー名がそもそも分からない。今回はこれらの問題を解決したため、備忘録的に書いておく。

Google Fontsのフォントをテスト環境に読み込む

google_fontsのソースを漁ってみると、FontFamilyの文字列にはとある命名規則が存在することが発覚した。しかしその命名規則がドキュメント化されておらずよくわからなかったため、各フォント用のメソッドを利用してTextStyleを作成し、その中にあるfontFamilyの文字列を抽出することにした。

Future<void> testExecutable(FutureOr<void> Function() testMain) async {
  setUpAll(() async {
    final Future<ByteData> fontRegular = rootBundle.load('assets/google_fonts/Murecho/Murecho-Regular.ttf');
    final Future<ByteData> fontBold = rootBundle.load('assets/google_fonts/Murecho/Murecho-Bold.ttf');
    
    final FontLoader fontLoader = FontLoader(GoogleFonts.murecho().fontFamily!)
      ..addFont(fontRegular)
      ..addFont(fontBold); // 各バリアントは同じFontLoaderのインスタンスに追加していく
    await fontLoader.load();
  });

  await testMain();
}

image.webp

上手くいった。

Material Symbolsのアイコンフォントをテスト環境に読み込む

続いて、material_symbols_iconsのアイコンが表示されない問題に対処していく。 このパッケージのソースを読むと、「MaterialSymbolsOutlined」「MaterialSymbolsRounded」「MaterialSymbolsSharp」の3つのフォントファミリーを読み込んでいることがわかる。そして実際のフォントファイルはパッケージのrootからlib/fonts/MaterialSymbols*.ttfに存在している。これらのファイルはパッケージをpubspecの依存関係に追加することでrootBundleから読み出せるようになるため、以下のように書くことでアイコンフォントを反映させられる。

※「MaterialSymbolsOutlined」の部分は使用しているものに合わせて書き換え

Future<void> testExecutable(FutureOr<void> Function() testMain) async {
  setUpAll(() async {
    final Future<ByteData> font = rootBundle.load('packages/material_symbols_icons/lib/fonts/MaterialSymbolsOutlined.ttf');
    
    final FontLoader fontLoader = FontLoader("packages/material_symbols_icons/MaterialSymbolsOutlined") // <--
      ..addFont(font);
    await fontLoader.load();
  });

  await testMain();
}

image.webp

上手くすべてのフォントを表示させることができた。