Типографическая система Flutter: размеры, веса и стили текста

Типографическая система Flutter — это набор согласованных ролей, размеров, начертаний, цветов и интервалов, который помогает управлять текстом во всём приложении. В основе лежат TextTheme и TextStyle: первый задаёт семантические уровни, второй описывает конкретное оформление. Ниже разберём, как выбрать параметры, переопределить тему и избежать типичных ошибок.
Что такое типографическая система Flutter
Типографическая система во Flutter описывает не отдельный шрифт, а правила его применения. Она связывает смысл текста с визуальным оформлением: крупный display используется для выразительного экрана, headline — для заголовка раздела, title — для названия компонента, body — для основного чтения, а label — для кнопок, вкладок и коротких подписей. Такой подход делает интерфейс цельным и упрощает поддержку.
Главная идея — не задавать параметры вручную в каждом виджете. Вместо десятков независимых значений команда создаёт ограниченную шкалу стилей и обращается к ней по назначению. Если бренд меняет шрифт или базовый размер, достаточно обновить тему, а не искать все текстовые элементы по проекту.
Из чего состоит TextStyle
TextStyle — объект, который хранит визуальные свойства текста. На практике чаще всего настраивают fontFamily, fontSize, fontWeight, height, letterSpacing, color и fontStyle. Эти параметры отвечают соответственно за семейство шрифта, размер, толщину, высоту строки, межбуквенный интервал, цвет и курсивное начертание.
Размер fontSize задаётся в логических пикселях, а не в физических точках печати. FontWeight выбирает доступную толщину из диапазона FontWeight.w100–FontWeight.w900. Свойство height задаёт отношение высоты строки к размеру шрифта: значение 1.4 при размере 16 формирует приблизительную высоту строки 22,4 логических пикселя. Если шрифт или платформа не поддерживает конкретный вес, система может использовать ближайший доступный вариант.
Свойство inherit определяет, могут ли дочерние текстовые стили наследовать параметры родительского текста. Для сложных компонентов также важны textBaseline, locale, decoration и fontVariations. Последнее особенно полезно для вариативных шрифтов, когда нужная толщина или ширина задаётся через оси шрифта, а не только через стандартные значения FontWeight.
Роли TextTheme в Material 3
TextTheme группирует текстовые стили по семантическим ролям. В актуальной схеме Material 3 используются три уровня display, headline, title, body и label, каждый с вариантами Large, Medium и Small. Роль выбирают не по внешнему виду, а по функции элемента. Например, название карточки обычно относится к title или headline, а длинное описание — к body.
- displayLarge, displayMedium и displaySmall — самые крупные текстовые уровни для выразительных экранов и ключевых сообщений.
- headlineLarge, headlineMedium и headlineSmall — заголовки секций и заметные смысловые блоки.
- titleLarge, titleMedium и titleSmall — названия экранов, карточек, диалогов и элементов навигации.
- bodyLarge, bodyMedium и bodySmall — основной текст, пояснения, описания и вторичный контент.
- labelLarge, labelMedium и labelSmall — кнопки, вкладки, фильтры, бейджи и короткие подписи.
Стили TextTheme могут быть nullable, поэтому при обращении к ним в Dart часто используют оператор безопасного доступа и запасной стиль. Например, для основного текста подходит Theme.of(context).textTheme.bodyMedium. Если компонент должен гарантированно получить оформление, предусмотрите fallback через copyWith или явно передайте локальный TextStyle.
Как выбрать шкалу размеров шрифта
Шкала размеров должна быть небольшой и последовательной. Для интерфейса со стандартным плотным контентом можно начать с bodySmall около 12–13, bodyMedium около 14–16 и bodyLarge около 16–18 логических пикселей. Заголовки затем выстраиваются выше, например в диапазоне от 20 до 36 и более. Это не обязательные значения, а отправная точка, которую проверяют на реальном контенте и разных экранах.
Не стоит увеличивать размер только ради визуальной иерархии. Иерархию создают одновременно размер, вес, цвет, расстояние до соседних элементов и положение в композиции. Заголовок 24 пикселя с умеренным весом и достаточным отступом может выглядеть убедительнее, чем заголовок 32 пикселя, зажатый между плотными блоками.
Размер, высота строки и читаемость
Пара fontSize и height особенно важна для длинного текста. Слишком маленькая высота строки делает абзац плотным, а чрезмерная превращает его в набор разрозненных строк. Для body-текста обычно нужен более свободный интервал, чем для коротких labels. Проверяйте не одну строку, а несколько абзацев с длинными русскими словами, цифрами и ссылками.
Русская кириллица может занимать больше визуального пространства, чем латиница в том же размере. Поэтому макет следует проверять на реальных русскоязычных заголовках: переносы, высота карточек и ширина кнопок иногда меняются заметнее, чем при тестировании английского текста. TextScaler и системное увеличение текста также не должны ломать композицию.
Как использовать FontWeight и начертания
FontWeight отвечает за визуальную плотность текста, но не заменяет смысловую структуру. w400 обычно соответствует обычному начертанию, w500 или w600 подходят для умеренного акцента, а w700 часто используют для сильного выделения и заголовков. Конкретное отображение зависит от подключённого семейства: если у него нет нужного файла, браузер или движок может синтезировать либо заменить начертание.
Для интерфейса лучше ограничить число рабочих весов. Например, bodyMedium можно оставить обычным, titleMedium сделать полужирным, а labelLarge использовать для интерактивных элементов. Если каждый второй текст получает новый вес, контраст исчезает, а система становится трудной для контроля. Важно также проверить, что кириллические глифы присутствуют во всех выбранных начертаниях.
Цвет, контраст и вторичная информация
Типографика включает не только геометрию, но и цвет. Основной текст должен сохранять достаточный контраст с фоном, а вторичный цвет — отличаться от него не настолько сильно, чтобы информация стала незаметной. Для текста ошибок, ссылок и подсказок используйте смысловые цвета последовательно: одинаковая роль должна выглядеть одинаково на разных экранах.
Цвет удобно хранить в цветовой схеме темы, а не зашивать непосредственно в TextStyle. Тогда светлая и тёмная темы смогут менять оттенки без копирования компонентов. Локальное изменение цвета через style.copyWith(color: ...) оправдано для контекста, например для неактивной подписи или текста на цветной кнопке.
Настройка глобальной темы приложения
Глобальную типографику задают в ThemeData через textTheme. Базовый сценарий — создать TextTheme, определить нужные роли и передать его в MaterialApp или основной экран. Для частичного изменения существующей темы используют copyWith: такой подход сохраняет остальные роли и уменьшает риск случайно сбросить системные параметры.
Практический пример: для брендингового приложения можно выбрать собственное семейство шрифта, назначить bodyMedium размером 16, высоту строки 1.45 и обычный вес, а titleLarge сделать размером 24, высотой 1.2 и весом w700. Кнопкам при этом не обязательно наследовать стиль заголовка: для них лучше отдельно настроить labelLarge с компактной высотой строки.
Локальное переопределение стиля в виджетах
Локальный TextStyle нужен, когда элемент действительно имеет особый контекст: ссылка внутри абзаца, выделенная сумма, предупреждение или текст на контрастной поверхности. Начинайте с роли темы и меняйте только необходимое свойство. Например, bodyMedium.copyWith(fontWeight: FontWeight.w600) сохраняет размер, высоту строки и остальные параметры базового стиля.
Полная замена стиля через новый TextStyle уместна для независимых декоративных элементов или фирменных заголовков. В обычном контенте она часто приводит к расхождениям: один компонент получает другую высоту строки, другой — другой цвет, третий — системный шрифт. Чем ближе компонент к общей теме, тем полезнее наследование.
Практический пример типографики карточки
Представим карточку статьи. Заголовок карточки можно связать с titleMedium: размер около 18–20, вес w600 или w700, высота строки около 1.2. Метаданные вроде даты и категории подойдут для labelSmall или bodySmall с приглушённым цветом. Описание логично оформить через bodyMedium, чтобы сохранить комфортное чтение в две–четыре строки.
Если карточка содержит кнопку, её текст должен быть коротким и устойчивым к увеличению масштаба. Не полагайтесь только на фиксированную ширину: длинная локализация, крупный системный текст или доступное имя могут потребовать дополнительного места. Используйте внутренние отступы, перенос или адаптивную компоновку, а не уменьшайте шрифт до плохо читаемого размера.
Адаптивность и доступность текста
Хорошая типографическая система должна выдерживать разные размеры экрана, ориентации и настройки доступности. Не проектируйте интерфейс только под один скриншот с базовым масштабом текста. Проверяйте, как меняются переносы, высота кнопок, положение иконок, диалоги и нижняя навигация при увеличении системного TextScaler.
- Проверяйте заголовки на узком экране и при длинных названиях.
- Разрешайте тексту переноситься там, где это не разрушает смысл.
- Избегайте жёсткой высоты контейнеров для многострочного текста.
- Тестируйте светлую и тёмную тему на одинаковых текстовых ролях.
- Проверяйте контраст, фокус, семантические подписи и масштабирование.
- Не передавайте смысл только через жирность или цвет.
Подключение собственного шрифта
Собственный шрифт подключают как семейство с файлами нужных начертаний и регистрируют в конфигурации проекта. После этого семейство назначают глобально в теме или локально в отдельных стилях. Для качественного результата подготовьте как минимум те веса, которые реально используются в интерфейсе, и убедитесь, что шрифт содержит кириллицу, цифры, знаки пунктуации и необходимые символы.
Перед публикацией оцените размер файлов и количество начертаний. Неиспользуемые варианты увеличивают пакет приложения, а неполный набор символов вызывает неожиданные подстановки. Отдельно проверьте цифры в ценах, кавычки, тире, математические знаки и смешанный русско-латинский текст — именно на таких строках несовместимость гарнитуры заметна быстрее всего.
Частые ошибки в типографике Flutter
- Задавать fontSize и fontWeight напрямую в каждом Text без общей системы ролей.
- Использовать слишком много размеров, из-за чего исчезает понятная иерархия.
- Путать FontWeight с отдельным файлом начертания и не проверять фактический результат.
- Фиксировать высоту карточки или кнопки, не учитывая переносы и масштабирование.
- Забывать про кириллицу, из-за чего часть символов отображается другой гарнитурой.
- Менять цвет текста локально и нарушать контраст или правила светлой и тёмной темы.
- Подбирать стиль по названию, но не по назначению: например, использовать label для длинного абзаца.
Самая распространённая системная ошибка — смешать дизайн-токены и случайные исключения. Если значение не относится к роли темы, его трудно объяснить и повторно использовать. Перед добавлением нового стиля спросите, чем он отличается от существующего: размером, весом, цветом, контекстом или только привычкой разработчика.
Чеклист перед выпуском интерфейса
- Определите назначение каждой текстовой роли.
- Сократите шкалу до повторяемого набора размеров и весов.
- Настройте TextTheme в одном месте.
- Проверьте высоту строки на заголовках и длинных абзацах.
- Убедитесь, что выбранный шрифт содержит полный набор кириллических символов.
- Проверьте контраст текста на всех фоновых поверхностях.
- Протестируйте увеличенный системный масштаб и узкие экраны.
- Сравните светлую и тёмную темы на одинаковых компонентах.
Часто задаваемые вопросы
Какой стиль TextTheme использовать для обычного текста?
Для обычного текста чаще всего подходит bodyMedium, а для более заметного или крупного чтения — bodyLarge. Выбор зависит от плотности интерфейса и длины контента, поэтому проверьте стиль на реальных абзацах. Для коротких пояснений и вторичной информации может подойти bodySmall, но его не следует использовать для основного чтения на всех экранах.
Чем отличаются TextStyle и TextTheme во Flutter?
TextStyle описывает один набор параметров текста: размер, вес, цвет, высоту строки и другие свойства. TextTheme объединяет несколько TextStyle в систему семантических ролей, таких как bodyMedium, titleLarge или labelSmall. Поэтому TextTheme отвечает за согласованную структуру, а TextStyle — за конкретное оформление.
Можно ли использовать свои названия текстовых ролей?
Да, собственные роли можно организовать поверх TextTheme через отдельный объект дизайн-токенов, расширение темы или вспомогательные методы. Это удобно для брендинговых стилей, которых нет в стандартной схеме Material. При этом базовые роли TextTheme лучше сохранить для совместимости с готовыми Material-компонентами.
Какой FontWeight выбрать для заголовка?
Для заголовка часто используют w600 или w700, но универсального значения нет. Сначала проверьте реальную плотность выбранной гарнитуры: один шрифт выглядит выразительно уже на w600, другой требует w700. Не выбирайте вес только по цифре — оценивайте контраст с основным текстом, длину заголовка и наличие соответствующего файла.
Почему во Flutter текст выглядит иначе после подключения шрифта?
Причина обычно связана с метриками гарнитуры, отсутствующим начертанием, неполной кириллицей или синтезом веса. Разные шрифты при одинаковом fontSize имеют разную ширину символов, высоту заглавных букв и внутренние отступы. Проверьте регистрацию семейства, соответствие файлов весам и отображение русских букв, цифр и знаков пунктуации.
Как сделать типографику доступной?
Сделайте текст масштабируемым, не ограничивайте многострочные блоки жёсткой высотой и проверяйте интерфейс при увеличенном системном размере. Поддерживайте достаточный контраст и не передавайте смысл только цветом или жирностью. Кнопки, ссылки и ошибки должны оставаться понятными и различимыми при разных настройках отображения.
Нужно ли задавать height для каждого TextStyle?
Нет, height не обязан быть задан у каждого стиля. Его полезно фиксировать там, где нужна предсказуемая ритмика текста, особенно в body-стилях и повторяющихся компонентах. Для коротких labels и декоративных элементов можно оставить значение по умолчанию, если визуальная проверка показывает устойчивый результат.
Вывод
Типографическая система Flutter становится надёжной, когда роли TextTheme связаны с назначением контента, а размеры, веса, высота строки и цвета образуют ограниченный набор правил. Начните с базовой шкалы, проверьте кириллицу и доступность, затем добавляйте исключения только там, где они действительно нужны.
Если интерфейсу нужен собственный характер, подберите и подготовьте гарнитуру для проекта с помощью fontgenerator.ru. Создайте шрифт, проверьте его начертания и используйте получившуюся типографическую основу в единой теме Flutter.