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

Textの構造を理解する/Understanding the Structure of Text

Textの構造を理解する/Understanding the Structure of Text

Bd34d8c4bc464a2bec2792579c759cd1?s=128

Kakeru Nakabachi

November 29, 2021
Tweet

Transcript

  1. Textの構造を理解する Flutter Kaigi 2021 November 29-30, 2021

  2. Kakeru Nakabachi - batch CyberAgent, Inc 株式会社WinTicketでモバイルアプリをFlutterでリ プレイス中 About me

    2
  3. 本セッションのゴール Textの構造を理解して実装できるようになる💪💪 3

  4. アジェンダ • Text • Textの構造 • AndroidとiOSでTextを描画したときの問題について • チームで対応した問題の対処法 •

    まとめ 4
  5. Text 5

  6. Text 文字を表示するためのWidget • プロパティたち ◦ maxLines ◦ overflow ◦ strutStyle

    ◦ style ◦ textAlign ◦ textHeightBehavior ◦ ... https://api.flutter.dev/flutter/widgets/Text-class.html 6
  7. Text 文字を表示するためのWidget • プロパティたち ◦ maxLines ◦ overflow ◦ strutStyle

    ◦ style ◦ textAlign ◦ textHeightBehavior ◦ ... https://api.flutter.dev/flutter/widgets/Text-class.html debugLabel fontFamily fontFamilyFallback fontSize fontStyle fontWeight forceStrutheight hashCode height leading leadingDistribution 7 TextStyleのプロパティ
  8. Text 8 https://api.flutter.dev/flutter/painting/TextStyle-class.html

  9. Text 9 https://api.flutter.dev/flutter/painting/TextStyle-class.html

  10. Text 文字を表示するためのWidget • maxLines • overflow • strutStyle • style

    • textAlign • textHeightBehavior • ... https://api.flutter.dev/flutter/widgets/Text-class.html 10
  11. Text 文字を表示するためのWidget • maxLines • overflow • strutStyle • style

    • textAlign • textHeightBehavior • ... https://api.flutter.dev/flutter/widgets/Text-class.html 11
  12. Text 文字を表示するためのWidget • maxLines • overflow • strutStyle • style

    • textAlign • textHeightBehavior • ... https://api.flutter.dev/flutter/widgets/Text-class.html 12
  13. Textの構造 13

  14. Textの構造 このようなTextを表示した場合、どのような構造でTextが構築されて表示されて いるかをみていく Flutterのvisual debuggingツールを有効化 https://flutter.dev/docs/testing/debugging#visual-debugging https://docs.flutter.dev/development/tools/devtools/inspector 14

  15. Textの構造 実行結果 Android https://api.flutter.dev/flutter/painting/TextStyle-cl ass.html Textが確保している領域 15

  16. Textの構造 実行結果 Android https://api.flutter.dev/flutter/painting/TextStyle-cl ass.html Textのbaseline 16

  17. それぞれの線の意味 baseline ascent: baselineから上部までの高さ descent: baselineから下部までの高さ leading: ある行の下端から次の行の上端までの高さ、TextStyle.height * TextStyle.fontSize

    - TextStyle.fontSize これらを意識してTextをつくっていく。今回は以下の3つにフォーカスする height、leadingDistribution、textHeightBehavior Textの構造 AaBbGgJj 17
  18. Textの構造 • height 行の高さをフォントサイズの倍数で調整で きる nullと1.0は別の意味 18 https://api.flutter.dev/flutter/painting/TextStyle-class.html

  19. Textの構造 • leadingDistribution leadingを上下にどのように配置するかを決める。 asent descent Top leading Bottom leading

    19
  20. Textの構造 • leadingDistribution TextLeadingDistribution.proportionalと.evenがある leadingを上下にどのように配置するかを決める。nullの場合は TextLeadingDistribution.proportionalと同じ挙動 20

  21. Textの構造 • leadingDistribution leadingを上下にどのように配置するかを決める。 TextLeadingDistribution.evenにした場合はascentとdescentから上下に等 しくleadingが設定される 21

  22. Textの構造 • textHeightBehavior applyHeightToFirstAscentとapplyHeightToLastDescentのbool値を設定す ることでTextの上下のleadingを除くことができる デフォルトはtrueでleadingあり falseを設定してleadingを除いた場合、textHeightBehaviorは効かなくなる 22

  23. Textの構造 • heightとleadingDistributionとtextHeightBehavior Configuration 1 fontSizeとheight以外はすべてデフォル ト設定 leadingDistributionは TextLeadingDistribution.proportional 23

  24. Textの構造 • heightとleadingDistributionとtextHeightBehavior Configuration 2 Configuration 1に TextHeightBehavior.applyHeightToFir stAscentをfalseに設定 24

  25. Textの構造 • heightとleadingDistributionとtextHeightBehavior Configuration 3 leadingDistributionを TextLeadingDistribution.evenに設定 25

  26. Textの構造 • heightとleadingDistributionとtextHeightBehavior Configuration 4 Configuration 3の設定に TextHeightBehavior.applyHeightToLa stDescentにfalseを追加で設定したとき 26

  27. Textの構造 • 複数行の場合 27

  28. Textの構造 • 複数行の場合 height: 2.5を追加 28

  29. Textの構造 • 複数行の場合 TextHeightBehavior.applyHeightToFirstAscent: falseを追加 29

  30. Textの構造 • 複数行の場合 TextHeightBehavior.applyHeightToLastDescent: falseを追加 30

  31. Textの構造 • 複数行の場合 leadingDistributionにTextLeadingDistribution.proportionalの設定 31

  32. Textの構造 • 複数行の場合 leadingDistributionにTextLeadingDistribution.evenの設定 32

  33. Textの構造 • 複数行の場合 TextStyleのleadingDistributionとTextHeightBehaviorの leadingDistribution TextStyle側が優先されて使われる 33

  34. Textの構造 • 複数行の場合 TextStyleのleadingDistributionとTextHeightBehaviorの leadingDistribution TextStyle側が優先されて使われる 34

  35. AndroidとiOSでTextを描画したときの 問題について 35

  36. 問題1:OSごとにTextのサイズが違う • 実はAndroidとiOSでは同じフォントサイズなどstyleの設定でもTextの高さ が異なる ➢ 高さ固定のWidgetの中にTextなど配置した場合に、片方のOSでは収ま るけど、もう一方では収まらなかったり、OS間で異なったデザインにな る 両OSのデザインがある場合は大丈夫 弊チームではiOSのデザインしかなくどのように対応するか考える必要があった

    36
  37. 問題1:OSごとにTextのサイズが違う 実はiOSとAndroidでは同じフォントサイズなどの設定でもTextの高さが異なる Android 37

  38. 問題1:OSごとにTextのサイズが違う 実はiOSとAndroidでは同じフォントサイズなどの設定でもTextの高さが異なる iOS 38

  39. 問題1:OSごとにTextのサイズが違う なぜ起こる? OSによってleadingの高さが違う、、? →TextHeightBehaviorを設定して上下のleadingをなくしたらどうなるか AaBbGgJj 39

  40. 問題1:OSごとにTextのサイズが違う TextHeightBehaviorを設定してみる Android 40

  41. 問題1:OSごとにTextのサイズが違う TextHeightBehaviorを設定してみる iOS 41

  42. 問題1:OSごとにTextのサイズが違う AndroidとiOSでTextHeightBehaviorの設定の挙動が違いそう?バグ、、? Android iOS 42

  43. 問題1:OSごとにTextのサイズが違う 結論:fontFamilyを指定しなかった場合、OSごとに読み込まれるフォントが異 なり、ascentとdescentが異なるため Android Pixel 4a 和文フォント:Noto Sans Japanese? 欧文フォント:Roboto

    iOS iPhone 12 Pro 和文フォント:ヒラギノ角ゴシック 欧文フォント:San Francisco Pro 43
  44. 問題1:OSごとにTextのサイズが違う TextStyle.backgroundColorを設定して、Textの中でフォントが確保している領域 を見てみる。和文フォントで大きく確保する領域に違いがある Android iOS 44

  45. 問題1:OSごとにTextのサイズが違う TextStyle.backgroundColorを設定して、Textの中でフォントが確保している領域 を見てみる。和文フォントで大きく確保する領域に違いがある Android iOS Androidのフォントが確保している領域が iOSより広い 45

  46. 問題2:Androidだと文字が下寄りに見える https://github.com/flutter/flutter/issues/79931 46

  47. 問題2:Androidだと文字が下寄りに見える Android iOS 24.0 32.0 fontSize 47

  48. 問題2:Androidだと文字が下寄りに見える Androidの方がフォントがデフォルトで確保している ascentとdescentが大きく、ascentの方が大きいので 下寄りに見える Android iOS 48

  49. 問題2:Androidだと文字が下寄りに見える Androidの方がフォントがデフォルトで確保しているascentと descentが大きく、ascentの方が大きいので下寄りに 見える Android iOS 49

  50. チームで対応した問題の対処法 50

  51. チームで対応した問題の対処法 ▪ 一般的な対処法と思われるもの • AndroidとiOSで共通のascentとdescentが小さいフォントを使う • AndroidだけiOSのシステムフォントのようなascentとdescentが小さい フォントを使う 51

  52. チームで対応した問題の対処法 ▪ 一般的な対処法と思われるもの • AndroidとiOSで共通のascentとdescentが小さいフォントを使う • AndroidだけiOSのシステムフォントのようなascentとdescentが小さい フォントを使う ➢ 弊チームでの対応はこれ

    52
  53. チームで対応した問題の対処法 ▪ AndroidでiOSのシステムフォントのようなascentとdescentが小さいフォン トを使うためには Androidで角ゴシックを使えば両プラットフォームで全く同じTextにすることが できるが、、、 ➢ Androidユーザは普段と違うフォントを目にすることになって違和感か もしれない ➢

    ライセンス問題( https://developer.apple.com/forums/thread/89094 ) 53
  54. チームで対応した問題の対処法 ▪ Android用にascentとdescentが小さいフォントを用意する必要がある ➢ NotoSans CJKを使用 CJK = Chinese Japanese

    Korean 今はJapaneseのみが入ったファイルサイズの小さいNotoSansJapaneseがあ る https://fonts.google.com/noto/specimen/Noto+Sans+JP 54
  55. チームで対応した問題の対処法 ▪ Android用にascentとdescentが小さいフォントを用意する必要がある NotoSans CJKのままでは、中国語や韓国語など使わないフォントも多いので、サ ブセット化してフォントファイルサイズを削減 .otf → .woff →

    .ttf → サブセット化(JIS第1水準+JIS第2水準+その他) NotoSans CJK RegularとNotoSans CJK Boldを用意 サブセット化前 サブセット化後 34MB 3MB 55
  56. チームで対応した問題の対処法 ▪ Android用にascentとdescentが小さいフォントを用意する必要がある コンバートする段階でascentとdescentが除かれたフォントができあがることを 利用してiOSのフォントのような扱いをしている https://github.com/minoryorg/Noto-Sans-CJK-JP ライセンス的には商用利用可、改変可 https://fonts.google.com/noto/use#bundle-noto-fonts-with-your-native-app https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL 56

  57. チームで対応した問題の対処法 ▪ Android用にascentとdescentが小さいフォントを用意する必要がある フォントの指定方法 なにも指定がない場合、OSのデフォルトのシステムフォントが使われる 57 ← 最初に読み込もうとするフォントを指定 ← fontFamilyで対応できなかった文字を描画す

    るためのフォントを指定
  58. チームで対応した問題の対処法 ▪ Android用にascentとdescentが小さいフォントを用意する必要がある フォントの指定方法 用意したフォントは日本語表示用に、英語はシステムフォントでデフォルトで選 択されるRobotoを使いたい fontFamilyに何も指定しない場合→Robotoが使われる 日本語を表示しようとしたとき、Robotoだけでは対応できずfontFamilyFallback に設定されているフォントを読み込もうとする なので、今回は用意したフォントをfontFamilyFallbackに設定

    58
  59. チームで対応した問題の対処法 ▪ ボタンなどの垂直方向の中央に文字が表示されるようにleadingの調整 ascentとdescentが小さいフォントを使うことにより、 デフォルトのleadingが効 くようになり、文字が厳密にはまだ中心に配置されていない TextStyle.leadingDistributionにTextLeadingDistribution.evenを設定して、上 下に等しくleadingを配置して文字が垂直方向に中心に配置されるようにする 59 フォント変更前

    フォント変更後
  60. まとめ • Textは様々な要素で構成されている • AndroidとiOSではフォントを指定しない場合、読み込むシステムフォントが それぞれ違うため、全く同じ見た目のTextを描画しない ◦ 高さが違う ◦ 文字が下よりに見える

    • 対応方法の1つにAndroidのときはデフォルトでasecntとdescentが大きいシ ステムフォントが使われるため、それらが小さいフォントを指定してiOSの 挙動に近づける • TextStyle.leadingDistributionにTextLeadingDistribution.evenを設定して 上下のleadingを均等に配分して文字を中央にする 60
  61. 参考 • https://flutter.dev/docs/testing/debugging#visual-debugging • https://flutter.dev/docs/development/tools/devtools/inspector • https://stackoverflow.com/questions/56799068/what-is-the-strutstyl e-in-the-flutter-text-widget • https://api.flutter.dev/flutter/painting/TextStyle-class.html

    • https://github.com/flutter/flutter/issues/79931 • https://fonts.google.com/noto/use#bundle-noto-fonts-with-your-nativ e-app • https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL • https://github.com/minoryorg/Noto-Sans-CJK-JP 61
  62. Thank you 62