Upgrade to Pro — share decks privately, control downloads, hide ads and more …

そのデザイントークン、Flutterでも同じ見た目になりますか?

Avatar for nakamizu nakamizu
October 29, 2026

 そのデザイントークン、Flutterでも同じ見た目になりますか?

FlutterKaigi 2026 Day1 の登壇資料です。
Webとアプリで同じデザイントークンを使っているのに、文字間だけ見た目がそろわなかった話と、その解決策を紹介します。

Avatar for nakamizu

nakamizu

October 29, 2026

Other Decks in Programming

Transcript

  1. WebとFlutterへ配布するデザイントークン 背景 › 課題 › 解決策 › 結果 › まとめ

    WebとFlutterのアプリで見た目をそろえるため、同じデザイントークンを配布している デザインシステム アプリ Web向けのスタイルとlint import Flutter向けのスタイルとlint import デザイントークン ©ASSIGN Inc. All Rights Reserved. 04
  2. デザイントークンとは 背景 › 課題 › 解決策 › 結果 › まとめ

    デザイントークンは、UIの細かい見た目を決める、デザインの最小単位 デザイントークン Aa 部品 色 タイポグラフィ 余白 角丸 影 グラデーション 線 アニメーション ©ASSIGN Inc. All Rights Reserved. 画面 05
  3. トークンの種類 背景 › 課題 › 解決策 › 結果 › まとめ

    配布しているデザイントークンは、色、タイポグラフィ、余白、角丸の4種類 色 ©ASSIGN Inc. All Rights Reserved. Aa タイポグラフィ 余白 角丸 06
  4. トークンの種類 背景 › 課題 › 解決策 › 結果 › まとめ

    色、余白、角丸と違い、タイポグラフィは見た目がそろわなかった 色 ©ASSIGN Inc. All Rights Reserved. Aa タイポグラフィ 余白 角丸 07
  5. Webとアプリの見比べ 背景 › 課題 › 解決策 › 結果 › まとめ

    同じデザイントークンを使っても、Webとアプリでテキストの詰まり方が違う Web アプリ https://xxx.jp ©ASSIGN Inc. All Rights Reserved. 08
  6. 文字間の書き方 背景 › 課題 › 解決策 › 結果 › まとめ

    ほかの3つと違い、文字間はFlutterでは比率で指定できない フォントサイズ font-size: 20px; fontSize: 20 太さ font-weight: 600; fontWeight: FontWeight.w600 行間 line-height: 1.5; height: 1.5 文字間 letter-spacing: 0.05em; letterSpacing: ? ©ASSIGN Inc. All Rights Reserved. 09
  7. Flutterの文字間 背景 › 課題 › 解決策 › 結果 › まとめ

    Flutterの文字間は、ピクセルの値でしか指定できない /// The amount of space (in logical pixels) to add between each letter. final double? letterSpacing; 出典:Flutter SDK 3.38.2 packages/flutter/lib/src/painting/text_style.dart ©ASSIGN Inc. All Rights Reserved. 10
  8. 比率をそのまま渡した場合 背景 › 課題 › 解決策 › 結果 › まとめ

    フォントサイズの5%のつもりで0.05を渡しても、文字間は0.05ピクセルになる letterSpacing: 1.0 letterSpacing: 0.05 ©ASSIGN Inc. All Rights Reserved. 11
  9. Webとの違い 背景 › 課題 › 解決策 › 結果 › まとめ

    比率で書けないので、フォントサイズごとに長さへ直す必要がある 1つ書けば、ブラウザが計算する CSS letter-spacing: 0.05em; ©ASSIGN Inc. All Rights Reserved. サイズごとに、自分で計算して書く Dart TextStyle(fontSize: 16, letterSpacing: 0.8) TextStyle(fontSize: 20, letterSpacing: 1.0) 12
  10. 換算の関数 背景 › 課題 › 解決策 › 結果 › まとめ

    トークンは比率のままWebと共有し、Flutterへ渡す直前にピクセルへ換算する Web letter-spacing: 0.05em; デザイントークン 0.05(フォントサイズの5%) Flutterへ渡す直前に換算 換算の関数 文字間 = 比率 × フォントサイズ static double px(double em, double fontSize) => em * fontSize; Flutter letterSpacing: 1.0 フォントサイズ20pxのとき ©ASSIGN Inc. All Rights Reserved. 13
  11. テキストスタイルの関数 背景 › 課題 › 解決策 › 結果 › まとめ

    テキストスタイルごとに関数を用意し、その中でフォントサイズと文字間を一緒に決める abstract final class DsTextStyles { static TextStyle display20W6({…}) { … } static TextStyle display24W6({…}) { … } … static TextStyle textNormal14W4({…}) { … } static TextStyle textNormal16W6({…}) { … } … } ©ASSIGN Inc. All Rights Reserved. 14
  12. 文字間が決まる流れ 背景 › 課題 › 解決策 › 結果 › まとめ

    20と0.05が関数に渡り、計算されて、文字間は1ピクセルになる 1. フォントサイズが20の関数 static TextStyle display20W6({double Function(double value)? scale}) { final fontSize = (scale ?? _spScale)(DsFontSize.s20); return TextStyle( fontSize: fontSize, fontWeight: DsFontWeight.semibold, letterSpacing: DsLetterSpacing.px(DsLetterSpacing.normalEm, fontSize), height: DsLineHeight.normal, … ); } ©ASSIGN Inc. All Rights Reserved. 15
  13. 文字間が決まる流れ 背景 › 課題 › 解決策 › 結果 › まとめ

    20と0.05が関数に渡り、計算されて、文字間は1ピクセルになる 2. fontSizeに20、normalEmに0.05が入る static TextStyle display20W6({double Function(double value)? scale}) { final fontSize = (scale ?? _spScale)(DsFontSize.s20); return TextStyle( fontSize: 20, fontWeight: DsFontWeight.semibold, letterSpacing: DsLetterSpacing.px(0.05, 20), height: DsLineHeight.normal, … ); } ©ASSIGN Inc. All Rights Reserved. 16
  14. 文字間が決まる流れ 背景 › 課題 › 解決策 › 結果 › まとめ

    20と0.05が関数に渡り、計算されて、文字間は1ピクセルになる 3. 関数で計算される static TextStyle display20W6({double Function(double value)? scale}) { final fontSize = (scale ?? _spScale)(DsFontSize.s20); return TextStyle( fontSize: 20, fontWeight: DsFontWeight.semibold, letterSpacing: 1.0, height: DsLineHeight.normal, … ); } ©ASSIGN Inc. All Rights Reserved. 17
  15. Lintルール 背景 › 課題 › 解決策 › 結果 › まとめ

    TextStyleにフォントサイズや文字間を数字で書くと、Lintルールが警告する name_label.dart return const Text( '私の名前は中水優輝です', style: TextStyle(fontSize: 20, letterSpacing: 0.05), ); ターミナル $ dart run custom_lint lib/name_label.dart:10:34 • Typography値を直書きしないでください。Typographyトークンを使用してください。 • avoid_raw_typography • WARNING lib/name_label.dart:10:53 • Typography値を直書きしないでください。Typographyトークンを使用してください。 • avoid_raw_typography • WARNING ©ASSIGN Inc. All Rights Reserved. 18
  16. Webとアプリの見比べ 背景 › 課題 › 解決策 › 結果 › まとめ

    Webとアプリで、同じテキストスタイルを選ぶだけで文字の見た目を統一できた Web 記事のタイトル https://xxx.jp ©ASSIGN Inc. All Rights Reserved. アプリ style: DsTextStyles.textNormal16W6(), 19
  17. Webとアプリの見比べ 背景 › 課題 › 解決策 › 結果 › まとめ

    Webとアプリで、同じテキストスタイルを選ぶだけで文字の見た目を統一できた Web 読了の目安 https://xxx.jp ©ASSIGN Inc. All Rights Reserved. アプリ style: DsTextStyles.textNormal14W4(), 20
  18. まとめ 背景 › 課題 › 解決策 › 結果 › まとめ

    トークンでは意味を共有し、Flutter向けの変換はデザインシステムに集約する 共有するのは意味 デザイントークン 意味:フォントサイズの5% ©ASSIGN Inc. All Rights Reserved. 変換はここに集約 デザインシステム 比率 × フォントサイズで換算 Lintで換算の漏れを防ぐ Web 0.05em のまま書ける Flutter ピクセルで受け取る 21
  19. 文字間以外の違い 背景 › 課題 › 解決策 › 結果 › まとめ

    文字間のほかにも、Webと違うところがあった 項目 カラーコード #FF9000CC 透明度が最後 Color(0xCCFF9000) 余白 padding: 16px; Padding(padding: …, child: Text(…)) 影のぼかし box-shadow: 0 0 10px BoxShadow(blurRadius: 10) ©ASSIGN Inc. All Rights Reserved. 要素自身に書く ぼかしの標準偏差 5 透明度が最初 Textには余白の引数がなく、外から包む ぼかしの標準偏差 約6.27 22
  20. おわりに 背景 › 課題 › 解決策 › 結果 › まとめ

    Webとの違いを楽しみながら、 これからもFlutterを学習していく ©ASSIGN Inc. All Rights Reserved. 23