Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Text 5

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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のプロパティ

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Textの構造 13

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

それぞれの線の意味 baseline ascent: baselineから上部までの高さ descent: baselineから下部までの高さ leading: ある行の下端から次の行の上端までの高さ、TextStyle.height * TextStyle.fontSize - TextStyle.fontSize これらを意識してTextをつくっていく。今回は以下の3つにフォーカスする height、leadingDistribution、textHeightBehavior Textの構造 AaBbGgJj 17

Slide 18

Slide 18 text

Textの構造 ● height 行の高さをフォントサイズの倍数で調整で きる nullと1.0は別の意味 18 https://api.flutter.dev/flutter/painting/TextStyle-class.html

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Textの構造 ● 複数行の場合 27

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

問題1:OSごとにTextのサイズが違う 結論:fontFamilyを指定しなかった場合、OSごとに読み込まれるフォントが異 なり、ascentとdescentが異なるため Android Pixel 4a 和文フォント:Noto Sans Japanese? 欧文フォント:Roboto iOS iPhone 12 Pro 和文フォント:ヒラギノ角ゴシック 欧文フォント:San Francisco Pro 43

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

チームで対応した問題の対処法 ■ Android用にascentとdescentが小さいフォントを用意する必要がある ➢ NotoSans CJKを使用 CJK = Chinese Japanese Korean 今はJapaneseのみが入ったファイルサイズの小さいNotoSansJapaneseがあ る https://fonts.google.com/noto/specimen/Noto+Sans+JP 54

Slide 55

Slide 55 text

チームで対応した問題の対処法 ■ Android用にascentとdescentが小さいフォントを用意する必要がある NotoSans CJKのままでは、中国語や韓国語など使わないフォントも多いので、サ ブセット化してフォントファイルサイズを削減 .otf → .woff → .ttf → サブセット化(JIS第1水準+JIS第2水準+その他) NotoSans CJK RegularとNotoSans CJK Boldを用意 サブセット化前 サブセット化後 34MB 3MB 55

Slide 56

Slide 56 text

チームで対応した問題の対処法 ■ 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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

まとめ ● Textは様々な要素で構成されている ● AndroidとiOSではフォントを指定しない場合、読み込むシステムフォントが それぞれ違うため、全く同じ見た目のTextを描画しない ○ 高さが違う ○ 文字が下よりに見える ● 対応方法の1つにAndroidのときはデフォルトでasecntとdescentが大きいシ ステムフォントが使われるため、それらが小さいフォントを指定してiOSの 挙動に近づける ● TextStyle.leadingDistributionにTextLeadingDistribution.evenを設定して 上下のleadingを均等に配分して文字を中央にする 60

Slide 61

Slide 61 text

参考 ● 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

Slide 62

Slide 62 text

Thank you 62