FlutterのTextがFigmaのデザイン通りにならないことがある
↓ 左がFigma、右がアプリ実行時のスクリーンショット
↓ 赤がFigma、それ以外の色の部分が実行したアプリのスクリーンショット。アプリの方が、1pxほど上にずれている。
↓Flutterのソース(抜粋)。GoogleFontsでダウンロードできるNotoSansを利用。
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(title: Text(widget.title)),
body: SafeArea(
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
// vertical: 3,
horizontal: 8,
),
decoration: const BoxDecoration(
color: Color(0xFFD9D9D9),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child: const Text(
// '1セット(¥2,000/1kg)を購入する ',
'ABCDEFGabcdefg',
style: TextStyle(
fontFamily: 'NotoSans',
fontWeight: FontWeight.w400,
fontSize: 16,
height: 16.0 / 16.0,
// height: 18.0 / 16.0,
),
),
),
),
),
);
}
}
↓pubspec.yaml
name: flutter_samples_2
description: A new Flutter project.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: '>=2.18.4 <3.0.0'
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
uses-material-design: true
assets:
- assets/
generate: true
fonts:
- family: NotoSans
fonts:
- asset: fonts/NotoSans-Regular.ttf
- asset: fonts/NotoSans-Bold.ttf
weight: 700
↓ Figmaの設定(同様にfontSize等を指定し、NotoSansを利用)
-
Flutter側でheight: 1.0にしているのが良くない?余裕を持たせた方がいい?-
height: 1.0
ではなくheight: 18.0 / 16.0
としてみたけど、ズレの大きさは変わらなかった。 - FigmaもFlutterもheightを指定しなければ、font metricsの定義が適用されるはず。その場合、同じように表示されるはず。
- やってみたところ...
- Figmaでは、文字の領域の中で、「A」が垂直方向に中央になるようになっている
- Flutterでは、文字の領域の中で、上に広がっている文字も、下に広がっている文字も、できる限り全ての文字が領域内に入るよう表示している気がする
- そもそも、どちらの場合も、fontSize: 16pxで height: 16pxになるようにしているのに、なぜ、gの下の部分がはみ出るのか???
- fontSizeのことを正しく理解できてないのかも。
- やってみたところ...
-
- htmlだとどうなる?→下のコメント参照。
あとで読む読んでみた: TextStyle class
- Line Height
- デフォルトでは、font metrics(font metricsについては下の「用語の整理」のコメント参照)で定義されたline heightでレイアウトされる。このline heightは、(利用するfontによって)font sizeより大きかったり小さかったりする。
By default, text will layout with line height as defined by the font. Font-metrics defined line height may be taller or shorter than the font size. The height property allows manual adjustment of the height of the line as a multiple of fontSize.
- line heightは、TextStyleのheightで制御可能。fontSize: 14かつheight: 2.0にすると、14*2.0で28.0pxのline heightになる。
-
このページで触れられているStrutStyleとは?関係ある?→今回関係ない。See StrutStyle for further control of line height at the paragraph level.
- 今回関係なさそう。異なるline heightの文字を含む、複数行のテキストの各行の高さを揃えるのに利用するみたい。StrutStyle classの例を見るのが早い。
Here, strut is used to absorb the additional line height in the second line.
- 各行の先頭に、StrutStyleで指定したスタイルの文字が挿入されたかのように振る舞う。それにより各行の高さを揃えることが可能になる。
Defines the strut, which sets the minimum height a line can be relative to the baseline.
Strut applies to all lines in the paragraph. Strut is a feature that allows minimum line heights to be set. The effect is as if a zero width space was included at the beginning of each line in the paragraph.
- 各行の先頭に、StrutStyleで指定したスタイルの文字が挿入されたかのように振る舞う。それにより各行の高さを揃えることが可能になる。
- 今回関係なさそう。異なるline heightの文字を含む、複数行のテキストの各行の高さを揃えるのに利用するみたい。StrutStyle classの例を見るのが早い。
- デフォルトでは、font metrics(font metricsについては下の「用語の整理」のコメント参照)で定義されたline heightでレイアウトされる。このline heightは、(利用するfontによって)font sizeより大きかったり小さかったりする。
- Leading Distribution and Trimming
- Leadingとは、隣り合う行と行の間の隙間。Leading = line height - (font's ascent + font's descent)。line heightが小さい場合、Leadingは負の値になることがある。
→ ⭐️デフォルトのline heightで表示すれば、Figmaのデザインと一致するのかも。TODO:確認。→一致しない。Leading is the vertical space between glyphs from adjacent lines.Quantitatively, it is the line height (see the previous section) subtracted by the font's ascent and descent. It's possible to have a negative Leading if height is sufficiently small.
- 用語整理
- font's ascent: Wikipediaでいうところのascent span, ascender height。(下のコメント参照)
- font's descent: Wikipediaでいうところのdescent span, descender height。(下のコメント参照)
- 用語整理
- heightが未指定の場合、font's metricsに従ってLeadingは上と下に配分される。heightを指定している場合、leadingDistribtionとtextHeightBehaviorで配分を制御できる。
When the height multiplier is null, leading and how it is distributed is up to the font's metrics. When the height multiplier is specified, the exact behavior can be configured via leadingDistribution and TextPainter.textHeightBehavior.
- leadingDistributionは、heightが指定された場合、テキストの高さを測って、line heightからテキストの高さを引いた余りを、設定されたTextLeadingDistribution enumに応じてテキストの上下に分配する。
How the vertical space added by the height multiplier should be distributed over and under the text.
When a non-null height is specified, after accommodating the glyphs of the text, the remaining vertical space from the allotted line height will be distributed over and under the text, according to the leadingDistribution property.
leadingDistribtion property- leadingDistributionには、propotionalとevenのどちらかを指定できる。heightを指定した場合、propotionalならfont's ascentの高さとfont's descentの高さの比率に応じてLeadingをテキストの上下に分配、evenなら上下均等に分配。
- ?: TextStyle classのConfiguration 3の図が間違っている気がする。TODO: 動作確認。
- evenを指定したら、今回のケース、うまくいかないかな?
- 概ねうまくいった。Leadingをテキストの上と下に分配する際の、html(ブラウザ)とFlutter間の挙動の違いによるものみたい。htmlはLeadingを上と下に均等に分配する(even)。Flutterはascent spanとdescent spanの比率に応じて分配する(proportional)。⭐️
- 概ねうまくいった。Leadingをテキストの上と下に分配する際の、html(ブラウザ)とFlutter間の挙動の違いによるものみたい。htmlはLeadingを上と下に均等に分配する(even)。Flutterはascent spanとdescent spanの比率に応じて分配する(proportional)。⭐️
- leadingDistributionには、propotionalとevenのどちらかを指定できる。heightを指定した場合、propotionalならfont's ascentの高さとfont's descentの高さの比率に応じてLeadingをテキストの上下に分配、evenなら上下均等に分配。
- textHeightBehavior
- TextHeightBehaviorは、applyHeightToFirstAscentとapplyHeightToLastDescentを持つ。defaultは両方true。
By default both properties are true, and TextStyle.height is applied as normal. When set to false, the font's default ascent will be used.
textHeightBehavior property - heightが指定された場合効く。このapplyHeightToFirstAscentがtrueだと、「the ascent of the first line」(Top Leadingのことかな)にLeadingが適用される。
When true, the TextStyle.height modifier will be applied to the ascent of the first line. When false, the font's default ascent will be used and the TextStyle.height will have no effect on the ascent of the first line.
This property only has effect if a non-null TextStyle.height is specified.
applyHeightToFirstAscent property
- TextHeightBehaviorは、applyHeightToFirstAscentとapplyHeightToLastDescentを持つ。defaultは両方true。
- leadingDistributionは、heightが指定された場合、テキストの高さを測って、line heightからテキストの高さを引いた余りを、設定されたTextLeadingDistribution enumに応じてテキストの上下に分配する。
- Leadingとは、隣り合う行と行の間の隙間。Leading = line height - (font's ascent + font's descent)。line heightが小さい場合、Leadingは負の値になることがある。
あとで読む: Figma Learn | Guide to text ← 一旦読まなくていいかも。下のコメントの通り、Flutterの問題のようであるため。
htmlでもやってみた。「A」の上下の隙間を見ると、Figmaに近い。やはりFlutterの問題みたい。
styleはFigmaからコピペした。
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans&display=swap" rel="stylesheet">
<style>
.container {
width: 148px;
height: 24px;
flex-shrink: 0;
border-radius: 12px;
background: #D9D9D9;
display: flex;
align-items: center;
justify-content: center;
}
.label {
color: #000;
text-align: center;
font-family: Noto Sans;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 16px; /* 100% */
}
</style>
</head>
<body>
<div class="container">
<p class="label">ABCDEFGabcdefg</p>
</div>
</body>
</html>
用語の整理
- Font metricsは、Fontにふくまれる文字全体もしくはそれぞれの文字についてのサイズやスペースなどの数値からなる。Font-wide metrics(そのフォントの文字全体に適用されるmetrics)は、cap height(大文字の高さ)、x-height(小文字の高さ)、ascender height, descender depth, font bounding boxなどの数値からなる。Glyph-level metrics(それぞれの文字ごとのmetrics)は...略。
Font metrics refers to metadata consisting of numeric values relating to size and space in the font overall, or in its individual glyphs. Font-wide metrics include cap height (the height of the capitals), x-height (the height of the lowercase letters) and ascender height, descender depth, and the font bounding box. Glyph-level metrics include the glyph bounding box, the advance width (the proper distance between the glyph's initial pen position and the next glyph's initial pen position), and sidebearings (space that pads the glyph outline on either side). Many digital (and some metal type) fonts are able to be kerned so that characters can be fitted more closely; the pair "Wa" is a common example of this.
Metrics- ascenderは、mean lineよりも上にはみ出ている部分。
In typography and handwriting, an ascender is the portion of a minuscule letter in a Latin-derived alphabet that extends above the mean line of a font. That is, the part of a lower-case letter that is taller than the font's x-height.
Assender - descenderは、baselineよりも下にはみ出ている部分
a descender is the portion of a letter that extends below the baseline of a font.
Descender -
Typefaceにあった図。(緑の字は筆者)
The descent spans the distance between the baseline and the lowest descending glyph in a typeface, and the part of a glyph that descends below the baseline has the name descender. Conversely, the ascent spans the distance between the baseline and the top of the glyph that reaches farthest from the baseline. The ascent and descent may or may not include distance added by accents or diacritical marks.
- (ascender heightやdescender heightとは別に?) アクセントのための記号の分のheightが上下についかされることがあるみたい。
The ascent and descent may or may not include distance added by accents or diacritical marks.
Typeface - Typefaceはデザイン、Fontは実装。
A typeface is the underlying visual design that can exist in many different typesetting technologies, and a font is one of these implementations. In other words, a typeface is what you see and a font is what you use.
...
Another useful analogy is that a typeface is to a song as a font is to an MP3 file: A font is a manifestation of the typeface/song, but the typeface/song exists outside of the format.
TypefaceGoogle Fonts | Typefaces
- ascenderは、mean lineよりも上にはみ出ている部分。
- fontSizeというのは、ascender height + descender heightという認識でいいのかな。この辺はっきりしない。