我們都知道在 Flutter 中可以通過 fontFamily
來引入第三方字型,例如通常會將 svg 圖示轉換為 iconfont.ttf
來實現向量圖示的入,而一般情況下我們是不會設定 fontFamily
來使用第三方字型, 那預設情況下 Flutter 使用的是什麼字型呢?
會出現這個疑問,是因為有一天設計給我發了下面那張圖,問我 “為什麼應用在蘋果平臺上的英文使用的是 PingFang SC
字型而不是 .SF UI Display
” ? 正如下圖所示,它們的 G 字母在顯示效果上會有所差異,比如 平方的 G 有明顯的轉折線。
這時候我不禁產生的好奇,在 Flutter 中引擎預設究竟是如何選擇字型?
通過官方解釋,在
typography.dart
原始碼中可以看到,
- Flutter 預設在 Android 上使用的是
Roboto
字型; - 在 iOS 上使用的是
.SF UI Display
或者.SF UI Text
字型。
The default font on Android is Roboto and on iOS it is .SF UI Display or .SF UI Text (SF meaning San Francisco). If you want to use a different font, then you will need to add it to your app.
那理論上在 iOS 使用的就是 .SF UI Display
字型才對,因為如下原始碼所示,在 Typography
中當 platform
是 iOS
時,使用的就是 Cupertino
相關的 TextTheme
,而 Typography
中的 white
和 black
屬性最終會應用到 ThemeData
的 defaultTextTheme
、 defaultPrimaryTextTheme
和 defaultAccentTextTheme
中,所以應該是使用 .SF
相關字型才會,為什麼會顯示的是 PingFang SC
的效果?
factory Typography({
TargetPlatform platform = TargetPlatform.android,
TextTheme black,
TextTheme white,
TextTheme englishLike,
TextTheme dense,
TextTheme tall,
}) {
assert(platform != null || (black != null && white != null));
switch (platform) {
case TargetPlatform.iOS:
black ??= blackCupertino;
white ??= whiteCupertino;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
black ??= blackMountainView;
white ??= whiteMountainView;
}
englishLike ??= englishLike2014;
dense ??= dense2014;
tall ??= tall2014;
return Typography._(black, white, englishLike, dense, tall);
}
複製程式碼
為了搞清不同系統上字型的區別,在查閱了資料後可知:
-
預設在 iOS 上:
-
中文字型:
PingFang SC
-
英文字型:
.SF UI Text
、.SF UI Display
-
-
預設在 Android 上:
-
中文字型:
Source Han Sans
/Noto
-
英文字型:
Roboto
-
也就是就 iOS 上除了 .SF
相關的字型外,還有 PingFang
字型的存在,這時候我突然想起在之前的 《Flutter完整開發實戰詳解(十七、 實用技巧與填坑二)》 中,因為國際化多語言在 .SF
會出現顯示異常,所以使用了 fontFamilyFallback
強行指定了 PingFang SC
。
getCopyTextStyle(TextStyle textStyle) {
return textStyle.copyWith(fontFamilyFallback: ["PingFang SC", "Heiti SC"]);
}
複製程式碼
終於破案了,因為當 fontFamily
沒有設定時,就會使用 fontFamilyFallback
中的第一個值將作為首選字型,而在 fontFamilyFallback
中是順序匹配的,當fontFamily
和 fontFamilyFallback
兩者都不提供,則將使用預設平臺字型。
而在 1.12.13 版本下測試發現 .SF
導致的問題已經修復了,所以只需要將 fontFamilyFallback
相關的程式碼去除即可。
那在 iOS 上使用 .SF
字型有什麼好處? 按照網路上的說法是:
SF Text
的字距及字母的半封閉空間,比如"a"!
上半部分會更大,因其可讀性更好,適用於更小的字型;SF Display
則適用於偏大的字型。具體分水嶺就是20pt
, 即字型小於20pt
時用Text
,大於等於20pt
時用Display
。更棒的是由於
SF
屬於動態字型,Text
和Display
兩種字型族是系統動態匹配的,也就是說你不用費心去自己手動調節,系統自動根據字型的大小匹配這兩種顯示模式。
那能不能在 Android 上也使用.SF
字型呢?按照官方的說法:
- 在使用 Material package 時,在 Android 上使用的是 ·Roboto font· ,而 iOS 使用的是
San Francisco font(SF)
; - 在使用 Cupertino package 時,預設主題始終使用
San Francisco font(SF)
;
但是因為 San Francisco font license
限制了該字型只能在 iOS、macOS 或 tvOS 上執行使用,所以如果使用了 Cupertino 主題的話,在 Android 上執行時使用 fallback font。
所以你覺得能不能在 Android 上使用?
最後再補充下,在官方的 architecture 中有提到,在 Flutter 中的文字呈現邏輯是有分層的,其中:
- 衍生自 Minikin 的 libtxt 庫用於字型選擇,分隔行等;
- HartBuzz 用於字形選擇和成型;
- Skia作為 渲染 / GPU後端;
- 在 Android / Fuchsia 上使用 FreeType 渲染,在 iOS 上使用CoreGraphics 來渲染字型 。
那讀完本篇,你奇奇怪怪的知識點有沒有增加?