Material Design 3 в Compose

Jetpack Compose предлагает реализацию Material You и Material 3 Expressive – следующего поколения Material Design. M3 Expressive – это расширение Material Design 3, включающее основанные на исследованиях обновления тем, компонентов, анимации, типографики и других элементов. Все они призваны помочь вам создавать привлекательные и удобные продукты, которые понравятся пользователям. Также поддерживаются функции персонализации Material You, например динамические цвета. M3 Expressive дополняет визуальный стиль и системный интерфейс Android 16.

Ниже показано, как реализовать Material Design 3 на примере примера приложения Reply. Пример приложения Reply полностью основан на Material Design 3.

Пример приложения для ответов с использованием Material Design 3
Рисунок 1. Пример приложения для ответа с использованием Material Design 3

Зависимость

Чтобы начать использовать Material 3 в приложении Compose, добавьте зависимость Compose Material 3 в файлы build.gradle:

implementation "androidx.compose.material3:material3:$material3_version"

После добавления зависимости вы можете начать добавлять в приложения системы Material Design, в том числе цвета, типографику и формы.

Экспериментальные API

Некоторые API M3 считаются экспериментальными. В таких случаях нужно включить эту функцию на уровне функции или файла с помощью аннотации ExperimentalMaterial3Api:

// import androidx.compose.material3.ExperimentalMaterial3Api
@Composable
fun AppComposable() {
    // M3 composables
}

Тематическое оформление Material Design

Тема M3 содержит следующие подсистемы: цветовая схема, типографика и формы. Если вы измените эти значения, они автоматически отразятся в компонентах M3, которые вы используете для создания приложения.

Подсистемы Material Design: цвет, типографика и формы
Рисунок 2. Подсистемы Material Design: цвет, типографика и формы

В Jetpack Compose эти концепции реализованы с помощью функции M3 MaterialTheme composable:

MaterialTheme(
    colorScheme = /* ...
    typography = /* ...
    shapes = /* ...
) {
    // M3 app content
}

Чтобы задать тему для контента приложения, определите цветовую схему, типографику и формы, которые будут использоваться в приложении.

Цветовая схема

Основа цветовой схемы – набор из пяти основных цветов. Каждый из этих цветов относится к тональной палитре из 13 тонов, которые используются компонентами Material 3. Например, вот цветовая схема для светлой темы для Ответить:

Светлая цветовая схема в примере приложения Reply
Рисунок 3. Светлая цветовая схема примера приложения "Ответы"

Подробнее о цветовой схеме и ролях цветов…

Как сгенерировать цветовые схемы

Вы можете создать собственную палитру ColorScheme вручную, но часто проще сгенерировать ее на основе исходных цветов бренда. Это можно сделать с помощью инструмента Material Theme Builder, а также экспортировать код для создания тем Compose. Будут созданы следующие файлы:

  • Color.kt содержит цвета темы со всеми ролями, определенными для светлой и темной темы.

val md_theme_light_primary = Color(0xFF476810)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFC7F089)
// ..
// ..

val md_theme_dark_primary = Color(0xFFACD370)
val md_theme_dark_onPrimary = Color(0xFF213600)
val md_theme_dark_primaryContainer = Color(0xFF324F00)
// ..
// ..

  • Theme.kt содержит настройки светлой и тёмной цветовых схем, а также темы приложения.

private val LightColorScheme = lightColorScheme(
    primary = md_theme_light_primary,
    onPrimary = md_theme_light_onPrimary,
    primaryContainer = md_theme_light_primaryContainer,
    // ..
)
private val DarkColorScheme = darkColorScheme(
    primary = md_theme_dark_primary,
    onPrimary = md_theme_dark_onPrimary,
    primaryContainer = md_theme_dark_primaryContainer,
    // ..
)

@Composable
fun ReplyTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme =
        if (!darkTheme) {
            LightColorScheme
        } else {
            DarkColorScheme
        }
    MaterialTheme(
        colorScheme = colorScheme,
        content = content
    )
}

Чтобы поддерживать светлую и тёмную темы, используйте isSystemInDarkTheme(). В зависимости от системных настроек выберите светлую или темную цветовую схему.

Динамические цветовые схемы

Динамические цвета – ключевой элемент Material You. Алгоритм подбирает пользовательские цвета на основе обоев пользователя и применяет их к приложениям и системному интерфейсу. Эта цветовая палитра используется в качестве основы для создания светлой и темной цветовых схем.

Пример динамического оформления приложения "Ответ" на основе обоев (слева) и стандартного оформления приложения (справа)
Рисунок 4. Пример динамического оформления приложения "Ответ" на основе обоев (слева) и стандартного оформления (справа)

Динамические цвета доступны в Android 12 и более поздних версий. Если динамический цвет доступен, вы можете настроить динамическую тему ColorScheme. В противном случае используйте светлую или темную ColorScheme.

ColorScheme предоставляет функции для создания динамической светлой или темной цветовой схемы:

// Dynamic color is available on Android 12+
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colors = when {
    dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current)
    dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current)
    darkTheme -> DarkColorScheme
    else -> LightColorScheme
}

Использование цвета

Вы можете получить доступ к цветам темы Material в приложении с помощью следующих методов: MaterialTheme.colorScheme

Text(
    text = "Hello theming",
    color = MaterialTheme.colorScheme.primary
)

Каждая роль цвета может использоваться в разных местах в зависимости от состояния, заметности и акцента компонента.

  • Основной цвет используется для главных компонентов, таких как заметные кнопки, активные состояния и оттенок приподнятых поверхностей.
  • Второстепенный ключевой цвет используется для менее заметных элементов интерфейса, таких как чипы фильтров, и расширяет возможности для цветового оформления.
  • Третий ключевой цвет используется для создания контрастных акцентов, которые могут уравновесить основной и дополнительный цвета или привлечь внимание к определенному элементу.

В дизайне примера приложения Reply используется цвет on-primary-container поверх primary-container, чтобы выделить выбранный элемент.

Основной контейнер и текстовые поля с цветом on-primary-container.
Рисунок 5. Основной контейнер и текстовые поля с цветом on-primary-container.

Card(
    colors = CardDefaults.cardColors(
        containerColor =
        if (isSelected) MaterialTheme.colorScheme.primaryContainer
        else
            MaterialTheme.colorScheme.surfaceVariant
    )
) {
    Text(
        text = "Dinner club",
        style = MaterialTheme.typography.bodyLarge,
        color =
        if (isSelected) MaterialTheme.colorScheme.onPrimaryContainer
        else MaterialTheme.colorScheme.onSurface,
    )
}

Здесь в панели навигации по ответам можно увидеть, как вторичные и третичные цвета контейнеров используются в контрасте, чтобы создать акцент.

Сочетание tertiary-container и on-tertiary-container для плавающей кнопки действия.
Рисунок 6. Сочетание tertiary-container и on-tertiary-container для плавающей кнопки действия.

Параметры текста

В Material Design 3 определена шкала шрифтов, включающая стили текста, адаптированные из Material Design 2. Названия и группировка упрощены: теперь есть только такие типы, как "Показ", "Заголовок", "Название", "Текст" и "Ярлык", а также размеры "Большой", "Средний" и "Маленький".

Шкала шрифтов по умолчанию для Material Design 3
Рисунок 7. Шкала типографики по умолчанию для Material Design 3
M3 Размер шрифта и высота строки по умолчанию
displayLarge Roboto 57/64
displayMedium Roboto 45/52
displaySmall Roboto 36/44
headlineLarge Roboto 32/40
headlineMedium Roboto 28/36
headlineSmall Roboto 24/32
titleLarge New- Roboto Medium 22/28
titleMedium Roboto Medium 16/24
titleSmall Roboto Medium 14/20
bodyLarge Roboto 16/24
bodyMedium Roboto 14/20
bodySmall Roboto 12/16
labelLarge Roboto Medium 14/20
labelMedium Roboto Medium 12/16
labelSmall New Roboto Medium, 11/16

Как задать типографику

Compose предоставляет класс M3 Typography, а также существующие классы TextStyle и для работы со шрифтами, чтобы моделировать шкалу шрифтов Material 3. Конструктор Typography предлагает значения по умолчанию для каждого стиля, поэтому вы можете опустить любые параметры, которые не хотите настраивать:

val replyTypography = Typography(
    titleLarge = TextStyle(
        fontWeight = FontWeight.SemiBold,
        fontSize = 22.sp,
        lineHeight = 28.sp,
        letterSpacing = 0.sp
    ),
    titleMedium = TextStyle(
        fontWeight = FontWeight.SemiBold,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.15.sp
    ),
    // ..
)
// ..

Body large, body medium и label medium для разных вариантов типографики.
Рисунок 8. Body large, body medium и label medium для разных вариантов использования типографики.

Для вашего продукта, скорее всего, не понадобятся все 15 стилей из шкалы типографики Material Design. В этом примере для уменьшенного набора выбрано пять размеров, а остальные опущены.

Вы можете настроить типографику, изменив значения по умолчанию для свойств TextStyle и , связанных со шрифтами, например fontFamily и letterSpacing.

bodyLarge = TextStyle(
    fontWeight = FontWeight.Normal,
    fontFamily = FontFamily.SansSerif,
    fontStyle = FontStyle.Italic,
    fontSize = 16.sp,
    lineHeight = 24.sp,
    letterSpacing = 0.15.sp,
    baselineShift = BaselineShift.Subscript
),

После того как вы определили Typography, передайте его в M3 MaterialTheme:

MaterialTheme(
    typography = replyTypography,
) {
    // M3 app Content
}

Как использовать стили текста

Вы можете получить типографику, предоставленную composable-функции M3 MaterialTheme, с помощью MaterialTheme.typography:

Text(
    text = "Hello M3 theming",
    style = MaterialTheme.typography.titleLarge
)
Text(
    text = "you are learning typography",
    style = MaterialTheme.typography.bodyMedium
)

Подробнее о том, как использовать типографику, можно узнать в руководстве по Material Design.

Фигуры

Поверхности материалов могут иметь разные формы. Фигуры привлекают внимание, обозначают компоненты, передают состояние и выражают бренд.

Масштаб формы определяет стиль углов контейнера, предлагая диапазон закруглений от квадратного до полностью круглого.

Как задать фигуры

В Compose есть класс M3 Shapes с расширенными параметрами, которые позволяют создавать новые фигуры M3. Масштаб M3 больше похож на масштаб шрифта, поэтому позволяет использовать в интерфейсе более разнообразные формы.

Фигуры бывают разных размеров:

  • Очень мелкий
  • Мелкий
  • Средний
  • Крупный
  • Очень крупный

По умолчанию у каждой фигуры есть значение по умолчанию, но вы можете его изменить:

val replyShapes = Shapes(
    extraSmall = RoundedCornerShape(4.dp),
    small = RoundedCornerShape(8.dp),
    medium = RoundedCornerShape(12.dp),
    large = RoundedCornerShape(16.dp),
    extraLarge = RoundedCornerShape(24.dp)
)

После того как вы определили Shapes, его можно передать в MaterialTheme M3:

MaterialTheme(
    shapes = replyShapes,
) {
    // M3 app Content
}

Как использовать фигуры

Вы можете настроить масштаб формы для всех компонентов в MaterialTheme или для каждого компонента отдельно.

Применить среднюю и большую фигуры со значениями по умолчанию:

Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(
    shape = MaterialTheme.shapes.large,
    onClick = {
    }
) {
    /* fab content */
}

Средний размер для карточки и большой размер для плавающей кнопки действия в приложении для ответов.
Рисунок 9. Средняя форма для карточки и большая форма для плавающей кнопки действия в примере приложения Reply

В Compose также есть два других символа: RectangleShape и CircleShape. Прямоугольник без радиуса скругления и круг с полностью скругленными краями:

Card(shape = RectangleShape) { /* card content */ }
Card(shape = CircleShape) { /* card content */ }

Ниже приведены примеры компонентов с примененными к ним значениями формы по умолчанию:

Значения форм по умолчанию для всех компонентов Material 3.
Рисунок 10. Значения форм по умолчанию для всех компонентов Material 3.

Подробнее о применении формы в Material Design…

Выделение

В M3 акцент создается с помощью сочетаний цветов и их оттенков. В M3 есть два способа сделать акцент на элементах интерфейса:

  • Используйте цвета surface, surface-variant и background вместе с цветами on-surface и on-surface-variants из расширенной цветовой системы M3. Например, surface можно использовать с on-surface-variant, а surface-variant – с on-surface, чтобы задать разные уровни акцента.
Использование нейтральных сочетаний цветов для акцента.
Рисунок 11. Использование нейтральных сочетаний цветов для акцента.
  • Использование разных вариантов начертания шрифта. Выше мы уже говорили о том, что для разных шрифтов можно задавать разные веса.

bodyLarge = TextStyle(
    fontWeight = FontWeight.Bold
),
bodyMedium = TextStyle(
    fontWeight = FontWeight.Normal
)

Высота над уровнем моря

В Material 3 высота обозначается в основном с помощью наложений тональных цветов. Это новый способ различать контейнеры и поверхности. В дополнение к теням используется тональное выделение: чем выше тональность, тем заметнее тон.

Тональное возвышение с возвышением тени
Рисунок 12. Тональная высота с высотой тениE

В Material 3 на тёмных темах наложения высоты заменены наложения тональных цветов. Цвет наложения берется из основного цвета.

Высота тени и высота тона в Material Design 3
Рисунок 13. Разница между высотой тени и высотой тона в Material Design 3

Компонент M3 Surface (composable-функция, лежащая в основе большинства компонентов M3) поддерживает как тональную, так и теневую высоту:

Surface(
    modifier = Modifier,
    tonalElevation = /*...
    shadowElevation = /*...
) {
    Column(content = content)
}

Компоненты материалов

В Material Design есть множество компонентов (например, кнопки, чипы, карточки, панель навигации), которые уже соответствуют принципам Material Theming и помогают создавать красивые приложения в стиле Material Design. Вы можете сразу начать использовать компоненты со свойствами по умолчанию.

Button(onClick = { /*..*/ }) {
    Text(text = "My Button")
}

В M3 предусмотрено множество версий одних и тех же компонентов, которые можно использовать в разных ролях в зависимости от того, на чем нужно сделать акцент.

Приоритет кнопок: плавающая кнопка действия, основная кнопка, текстовая кнопка
Рисунок 14. Приоритет кнопок: от плавающей кнопки действия и основной кнопки до текстовой кнопки
  • Расширенная плавающая кнопка действия для наиболее важного действия:

ExtendedFloatingActionButton(
    onClick = { /*..*/ },
    modifier = Modifier
) {
    Icon(
        imageVector = Icons.Default.Edit,
        contentDescription = stringResource(id = R.string.edit),
    )
    Text(
        text = stringResource(id = R.string.add_entry),
    )
}

  • Заполненная кнопка для действий, на которых нужно сделать акцент:

Button(onClick = { /*..*/ }) {
    Text(text = stringResource(id = R.string.view_entry))
}

  • Текстовая кнопка для действия, которое не нужно выделять:

TextButton(onClick = { /*..*/ }) {
    Text(text = stringResource(id = R.string.replated_articles))
}

Подробнее о кнопках и других компонентах Material… Material 3 предлагает широкий выбор наборов компонентов, таких как кнопки, панели приложений и навигационные компоненты, которые специально разработаны для разных вариантов использования и размеров экрана.

Кроме того, в Material есть несколько компонентов навигации, которые помогают реализовать навигацию в зависимости от размера экрана и состояния.

NavigationBar используется для компактных устройств, когда нужно настроить таргетинг на пять или менее целевых страниц:

NavigationBar(modifier = Modifier.fillMaxWidth()) {
    Destinations.entries.forEach { replyDestination ->
        NavigationBarItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { }
        )
    }
}

NavigationRail – для небольших и средних планшетов или телефонов в альбомной ориентации. Это повышает удобство использования устройств.

NavigationRail(
    modifier = Modifier.fillMaxHeight(),
) {
    Destinations.entries.forEach { replyDestination ->
        NavigationRailItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { }
        )
    }
}

Пример приложения "Ответы" с BottomNavigationBar(слева) и NavigationRail(справа)
Рисунок 15. Reply Showcase: BottomNavigationBar (слева) и NavigationRail (справа)

Ответы должны быть оформлены в соответствии с темой по умолчанию, чтобы пользователям было удобно читать их на устройствах любого размера.

NavigationDrawer – для планшетов среднего и большого размера, на которых достаточно места для показа подробной информации. Вы можете использовать как PermanentNavigationDrawer, так и ModalNavigationDrawer вместе с NavigationRail.

PermanentNavigationDrawer(modifier = Modifier.fillMaxHeight(), drawerContent = {
    Destinations.entries.forEach { replyDestination ->
        NavigationRailItem(
            selected = selectedDestination == replyDestination,
            onClick = { },
            icon = { },
            label = { }
        )
    }
}) {
}

Показ ответов на панели навигации
Рисунок 16. Показ ответа на постоянной панели навигации

Варианты навигации повышают удобство использования, эргономичность и доступность. Подробнее о компонентах навигации Material Design можно узнать в практической работе по адаптивному макету Compose.

Как настроить тему компонента

M3 поддерживает персонализацию и гибкость. Для всех компонентов заданы цвета по умолчанию, но при необходимости их можно изменить с помощью API.

Большинство компонентов, например карточки и кнопки, предоставляют объект по умолчанию, который позволяет изменять цвет и высоту:

val customCardColors = CardDefaults.cardColors(
    contentColor = MaterialTheme.colorScheme.primary,
    containerColor = MaterialTheme.colorScheme.primaryContainer,
    disabledContentColor = MaterialTheme.colorScheme.surface,
    disabledContainerColor = MaterialTheme.colorScheme.onSurface,
)
val customCardElevation = CardDefaults.cardElevation(
    defaultElevation = 8.dp,
    pressedElevation = 2.dp,
    focusedElevation = 4.dp
)
Card(
    colors = customCardColors,
    elevation = customCardElevation
) {
    // m3 card content
}

Подробнее о настройке Material 3…

Интерфейс системы

Некоторые аспекты Material You связаны с новым визуальным стилем и системным интерфейсом Android 12 и более поздних версий. Изменения коснулись двух основных областей: эффекта волны и эффекта достижения конца прокрутки. Для внедрения этих изменений никаких дополнительных действий не требуется.

Рябь

Теперь при нажатии на экран появляется легкая рябь. Compose Material Ripple использует RippleDrawable платформы на устройствах Android, поэтому эффект мерцания доступен на Android 12 и более поздних версиях для всех компонентов Material.

Разница между M2 и M3
Рисунок 17. Разница между M2 и M3

Эффект достижения конца прокрутки

Теперь при достижении конца прокрутки используется эффект растяжения на краю прокручиваемых контейнеров. По умолчанию растягивание при достижении конца прокрутки включено в composable-функциях контейнеров с прокруткой, например LazyColumn, LazyRow и LazyVerticalGrid, в Compose Foundation версии 1.1.0 и выше независимо от уровня API.

Прокрутка с эффектом растяжения на краю контейнера
Рисунок 18. Эффект достижения конца прокрутки с эффектом растяжения на краю контейнера

Специальные возможности

Стандарты специальных возможностей, встроенные в компоненты Material, предназначены для создания инклюзивного дизайна продуктов. Понимание того, насколько доступен ваш продукт, может повысить удобство его использования для всех пользователей, в том числе с нарушениями зрения, слуха, когнитивных функций или моторики, а также с временными ограничениями возможностей (например, сломанной рукой).

Специальные возможности: цвета

Динамические цвета соответствуют стандартам доступности в отношении контрастности. Система тональных палитр необходима для того, чтобы цветовая схема была доступна по умолчанию.

Система цветов Material содержит стандартные значения тонов и измерения, которые можно использовать для достижения доступных коэффициентов контрастности.

Пример приложения Reply: первичная, вторичная и третичная палитры тонов (сверху вниз)
Рисунок 19. Пример приложения "Ответы": первичная, вторичная и третичная палитры тонов (сверху вниз)

Все компоненты Material и динамическое оформление уже используют указанные выше роли цвета из набора тональных палитр, выбранных с учетом требований к доступности. Однако при настройке компонентов необходимо использовать подходящие роли цветов и избегать несоответствий.

Используйте on-primary поверх primary, on-primary-container поверх primary-container и так далее для других акцентных и нейтральных цветов, чтобы обеспечить доступный контраст для пользователя.

Использование третичного контейнера поверх основного приводит к тому, что кнопка имеет низкий контраст:

// ✅ Button with sufficient contrast ratio
Button(
    onClick = { },
    colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.primary,
        contentColor = MaterialTheme.colorScheme.onPrimary
    )
) {
}

// ❌ Button with poor contrast ratio
Button(
    onClick = { },
    colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.tertiaryContainer,
        contentColor = MaterialTheme.colorScheme.primaryContainer
    )
) {
}

Достаточная контрастность (слева) и недостаточная контрастность (справа)
Рисунок 20. Достаточная контрастность (слева) и недостаточная контрастность (справа)

Специальные возможности типографики

Шкала типа M3 обновляет статическую шкалу типов и значения, чтобы предложить упрощенную, но динамическую структуру категорий размеров, которые масштабируются на разных устройствах.

Например, в M3 размер "Маленький экран" может иметь разные значения в зависимости от контекста устройства, например телефона или планшета.

Большие экраны

В Material Design есть рекомендации по адаптивным макетам и складным устройствам, которые помогут сделать ваши приложения доступными и удобными для пользователей больших устройств.

В Material Design есть разные виды навигации, которые помогут вам сделать интерфейс приложения более удобным для пользователей больших экранов.

Ознакомьтесь с рекомендациями по обеспечению качества приложений для устройств с большим экраном и примером приложения "Ответ" с адаптивным и доступным дизайном.

Подробнее…

Чтобы узнать больше о тематическом оформлении Material Design в Compose, ознакомьтесь со следующими ресурсами:

Примеры приложений

Документы

Документация по API и исходный код

Видео