Эта небольшая статья посвящена теме отрисовки шрифтов в Flutter. Хотя это может показаться незначительной деталью, она важна при возникновении связанных проблем.
В этой статье мы быстро познакомимся с основами отрисовки шрифтов, решим некоторые проблемы, связанные с ними, а также рассмотрим несколько полезных приемов. Статья может оказаться достаточно объемной, поэтому рекомендуется отметить её для последующего чтения.
Сперва ответьте на вопрос, который я часто задаю на собеседованиях: Какие шрифты используются в Flutter на платформах Android и iOS?
Если вы знакомы с исходным кодом typography.dart
, то ваш первый вывод будет следующим:
Roboto
;.SF UI Display
или .SF UI Text
.Однако если вы углубитесь в эту тему, то узнаете, что:
PingFang SC
(для традиционного китайского — PingFang TC
, для гонконгского — PingFang HK
);.SF UI Text
или .SF UI Display
.Source Han Sans
или Noto
;Roboto
.Теперь вы можете спросить: Можно ли использовать шрифт PingFang
для отображения английских букв вместо .SF
? Ответ положительный, но формы и вес буквы могут немного отличаться. Например, буква G в этих двух шрифтах выглядит по-разному.
А что делать, если вам нужно отобразить китайские и корейские буквы одновременно? В этом случае шрифты PingFang
и .SF
недостаточны, и вам потребуется использовать универсальный шрифт, такой как Apple SD Gothic Neo
. Однако здесь стоит обратить внимание на одну ошибку, которую вы можете встретить в Flutter.
На приведённом ниже рисунке видно, что при использовании шрифта Apple SD Gothic Neo
для отображения китайских и корейских символов некоторые буквы могут выглядеть странно. Например, буквы «推» и «广». Буква «广», которая отсутствует в универсальном шрифте, отображается как китайская буква «广», тогда как буква «推» использует форму из универсального шрифта.
Для решения этой проблемы можно воспользоваться следующим простым методом: Добавьте в конфигурацию fontFamilyFallback
свойства TextStyle
или Theme
значения ["PingFang SC", "Heiti SC"]
.
Кроме того, если вы всё ещё сомневаетесь в различиях между шрифтами .SF UI Display
и .SF UI Text
, то вам не стоит слишком беспокоиться, так как приблизительно можно понять следующее:
.SF Text
подходит для более мелкого шрифта;.SF Display
подходит для крупного шрифта, границей может служить примерно 20 пунктов. Однако SF (San Francisco) является динамическим шрифтом, и система будет динамически подбирать наиболее подходящий вариант.# Второй раздел. Flutter Text
Хотя выше были рассмотрены некоторые аспекты шрифтов, есть и различия между ними в Flutter. В Flutter логика отображения текста имеет несколько уровней, среди которых:
А теперь представьте, что я спрошу вас: Какую высоту займет буква "H" со значением fontSize: 100
? Как бы вы ответили?
Сначала сравним красный контейнер размером 100 пикселей и текст "H" со значением fontSize: 100
. Можно заметить, что область синего цвета, где находится текст "H", должна быть больше области красного цвета.
Фактически, эта синяя область представляет собой высоту строки, которая зависит от параметра height
в объекте TextStyle
.
По умолчанию значение параметра height
равно null
, но когда мы его установим равным 1
, как показано ниже, можно видеть, что высота синей области совпадает с высотой красной области, то есть она становится 100 пикселей. Таким образом, буква "H" полностью помещается в синюю область.
Что такое
height
? В объекте TextStyle
значение параметра height
влияет на высоту строки следующим образом:
height
равно null
, высота строки по умолчанию определяется мерой шрифта (об этом подробнее позже);height
установлено, высота строки равна произведению значения height
и значения fontSize
;Например, сравнение синего и красного цветов показывает разницу высот при значениях height
равных null
и 1
.
Так что, прочитав это, вы узнали ещё один небольшой трюк: когда текст внутри контейнера с "ограниченной высотой" не может быть центрирован, можно попробовать изменить параметр height
в TextStyle
, чтобы добиться желаемого эффекта.
Конечно, если вы удалите параметр
height:50
у контейнера, то получится другой эффект.
Таким образом, параметр height
и высота отрисованного текста находятся в пропорциональной зависимости, как показано ниже:
Кроме того, в тексте помимо параметра height
в TextStyle
есть также параметр height
в StrutStyle
, который влияет на общую меру шрифта, то есть на высоту между вершинами (ascent
) и нижними границами (descent
).
Теперь скажите, чем отличается этот параметр height
в StrutStyle
от аналогичного параметра в TextStyle
? Как показывает следующий пример:
forceStrutHeight
в StrutStyle
установлен в true, параметр height
в TextStyle
не применяется;fontSize:50
в StrutStyle
влияет на содержание по-разному по сравнению с установкой параметра fontSize:100
в TextStyle
.Внутри StrutStyle
также имеется параметр leading
, при его использовании достигается полный контроль над высотой строки в Flutter. По умолчанию значение этого параметра равно null, а его эффект представляет собой множитель размера шрифта, распределённого равномерно сверху и снизу.
Так что, прочитав это, вы узнали ещё один небольшой трюк: установка параметра leading
позволяет равномерно распределять высоту, поэтому он также используется для регулировки межстрочных интервалов.
Для получения более подробной информации о высоте строки обратитесь к статье «Глубокое понимание «холодных» знаний о шрифтах в Flutter».
FontWeight
. Наверняка многие знакомы с этим параметром; например, стандартное значение normal
равно w400, а часто используемый параметр bold
равен w700. Весь диапазон значений параметра FontWeight
охватывает значения от 100 до 900. Ответ отрицательный, так как обычно, как показано на следующем рисунке, некоторые библиотеки шрифтов не поддерживают определенные значения Weight, например:
Вы можете задаться вопросом, почему мы подробно рассматриваем FontWeight? Это связано с тем, что в Flutter 3.0 есть проблема с отображением китайских символов!
На следующем рисунке видно, что в Flutter 3.0 отображение китайских символов со значением веса от 100 до 500 некорректно, глазами можно заметить, что все эти значения отображаются как одинаковый вес.
Эта проблема связана с тем, что при вызове метода
onMatchFamilyStyleCharacter
, реализация этого метода не выбирает наиболее подходящий шрифт, который соответствуетTextStyle
. Поэтому при вызове функцииCTFontCreateWithFontDescriptor
передается значение веса, но отсутствует имя семейства шрифтов (familyName
). В результате функцияCTFontCreateWithFontDescriptor
возвращает по умолчанию Helvetica шрифт.Простое временное решение заключается в том, чтобы глобально установитьfontFamilyFallback: ["Roboto"]
илиfontFamily: 'Roboto'
, что решает проблему, используя механизм fallback. Вы заметите, что ранее рассмотренные знания о шрифтах могут быть быстро применены здесь.
Поскольку китайский язык на iOS использует шрифт
PingFang SC
, достаточно использовать этот шрифт в качестве fallback для корректного отображения. Эта проблема проявляется на эмуляторах Android, устройствах iOS, Mac и других платформах, но на реальных устройствах Android она отсутствует. Я также сообщил о ней в #105014.
После добавления fallback эффект представлен на левой стороне приведённого выше рисунка. Какова же роль fallback?
Ранее мы уже говорили, что система использует различные библиотеки шрифтов для отображения нескольких языков, а когда нужный шрифт не находится, используется последовательность шрифтов, указанной в fallback, например:
Если требуемый шрифт не найден в fontFamily, поиск осуществляется в fontFamilyFallback. Если шрифт не найден ни там, ни там, возвращается дефолтный шрифт. Кроме того, в отношении
FontWeight
есть ещё один "сюрприз": на iOS, если пользователь включил "Жирный шрифт" в настройках доступности, то при использовании компонентаText
, все шрифты автоматически становятся жирными с весом w700.
Это происходит потому что внутри
Text
используется проверка MediaQuery.boldTextOverride
. Flutter получает информацию от пользователя iOS о том, что "Bold Font" активирован, и вынуждает установить fontWeight
как FontWeight.bold
. Однако, если вы используете RichText
, такого поведения не будет.
Здесь снова можно использовать небольшой трюк: если вы хотите избежать влияния этих системных действий, вы можете глобально отключить его, используя вложенные MediaQuery
. Аналогичным образом можно отключить textScaleFactor
и platformBrightness
.
return MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window).copyWith(boldText: false),
child: MaterialApp(
useInheritedMediaQuery: true,
),
);
И последнее, поговорим о малоизвестном параметре FontFeature
.
Что такое FontFeature
? Проще говоря, это свойство, которое влияет на форму символов шрифта, аналогично font-feature-settings
в области фронтенд-разработки. Он отличается от FontFamily
тем, что используется для указания параметров формы символов внутри шрифта.
На следующем рисунке показана сравнительная отрисовка параметров
frac
(дробь) иtnum
(табличные цифры). Такое поведение позволяет достичь специальных эффектов отображения без необходимости использования дополнительной библиотеки шрифтов. Также слово "Feature" может переводиться как "функциональность", поэтому можно сказать, что это характеристика шрифта.
А зачем нужен FontFeature
? Вот ещё одна хитрая идея: если цифры и текст расположены рядом и не выравниваются должным образом, вы можете использовать fontFeatures: [FontFeature('tnum')]
для выравнивания.Например, на следующем рисунке слева показано состояние без применения fontFeatures
, а справа — после применения FontFeature('tnum')
. Разница довольно заметна.
Для получения более подробной информации о
FontFeature
см. статью "Flutter: альтернативные способы работы со шрифтами: FontFeature".
В заключение стоит отметить, что данная статья содержит достаточно плотную информацию, которая включает:
Из вышеперечисленных аспектов были рассмотрены "холодные" знания и небольшие хитрости по работе с шрифтами в Flutter, такие как решение проблем с отображением шрифтов при использовании нескольких языков, правильная настройка высоты строки и работа с цифровым контентом.
Если у вас есть вопросы по поводу шрифтов, пожалуйста, оставьте свои комментарии для обсуждения!
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )