Темы и цвета Material 3

Разбираемся, как Material 3 задаёт единые цвета, шрифты и формы через MaterialTheme.

MaterialTheme — composable, который раздаёт всем вложенным элементам общую цветовую схему, типографику и формы.

Единый источник стиля

Вместо того чтобы прописывать цвета у каждого Text и Button, вы задаёте тему один раз наверху. Компоненты Material сами берут из неё нужные значения:

@Composable
fun App() {
    MaterialTheme {
        Surface {
            Text("Текст в цветах темы")
        }
    }
}

Surface здесь — фоновая «подложка», которая красит область цветом темы и задаёт уровень над фоном. Доступ к текущим значениям темы — через объект MaterialTheme:

Text(
    text = "Заголовок",
    color = MaterialTheme.colorScheme.primary,
    style = MaterialTheme.typography.headlineMedium
)

Цветовые роли, а не конкретные цвета

Material 3 оперирует ролями: primary (основной), secondary, background, surface, error и парные к ним onPrimary, onBackground (цвет текста поверх). Вы используете роль, а конкретный оттенок подставляет тема. Поэтому переключение на тёмную тему не требует переписывать каждый экран.

РольГде применяется
primaryакцентные элементы, основные кнопки
backgroundфон экрана
surfaceфон карточек и панелей
onPrimaryтекст/иконки поверх primary

Светлая и тёмная тема

Тему собирают из двух цветовых схем — светлой и тёмной — и выбирают нужную по системной настройке:

@Composable
fun AppTheme(content: @Composable () -> Unit) {
    val colors = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
    MaterialTheme(colorScheme = colors, content = content)
}

Как работает под капотом

MaterialTheme кладёт цвета, шрифты и формы в особый механизм Compose — CompositionLocal. Это «неявный контекст», доступный всем вложенным composable без передачи через параметры. Когда Text запрашивает MaterialTheme.colorScheme.primary, он читает ближайшее значение из этого контекста. Смена темы наверху автоматически перерисует всё, что эти значения читало.

Частые ошибки

  • Прописывать жёсткие цвета (Color(0xFF...)) вместо ролей — тёмная тема ломается.
  • Использовать primary для текста на нём же — нужен парный onPrimary, иначе текст сольётся с фоном.
  • Забыть обернуть приложение в MaterialTheme — компоненты получат значения по умолчанию.

Итог

  • MaterialTheme задаёт цвета, шрифты и формы один раз для всего дерева.
  • Используйте цветовые роли (primary, surface, onPrimary), а не конкретные оттенки.
  • Светлая и тёмная темы — это две схемы, выбираемые по системной настройке.
Проверьте себя
1. Почему в Material 3 используют цветовые роли (primary, surface), а не конкретные цвета?
AТак короче писать
BРоль подставляет нужный оттенок из темы, поэтому светлая и тёмная темы работают без переписывания экранов
CКонкретные цвета запрещены компилятором
DРоли быстрее рендерятся
2. Через какой механизм MaterialTheme раздаёт цвета вложенным composable?
AГлобальные переменные
BCompositionLocal — неявный контекст, доступный потомкам
CПередачу через каждый параметр вручную
DФайл ресурсов XML