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.

Пример приложения Reply с использованием Material Design 3
Рисунок 1. Пример приложения Reply, использующего 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 эти концепции реализованы с помощью composable-функции MaterialTheme из M3:

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

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

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

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

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

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

Генерировать цветовые схемы

Хотя вы можете создать пользовательскую 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. Алгоритм подбирает пользовательские цвета для приложений и интерфейса системы на основе обоев пользователя. Эта палитра используется в качестве основы для создания светлых и темных цветовых схем.

Динамическая тема на основе обоев (слева) и тема по умолчанию (справа) в примере приложения Reply
Рисунок 4. Динамическая тема на основе обоев (слева) и тема по умолчанию (справа) в примере приложения Reply

Динамические цвета доступны на устройствах с 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 Design в своем приложении через 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,
    )
}

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

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

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

В Material Design 3 определена шкала шрифтов, включая стили текста, адаптированные из Material Design 2. Названия и группировка были упрощены: теперь есть стили display, headline, title, body и label, и для каждого из них доступны размеры large, medium и small.

Шкала типографики по умолчанию для 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
    ),
    // ..
)
// ..

Крупный основной текст, средний основной текст и средний ярлык для разных вариантов использования типографики.
Рисунок 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-функции MaterialTheme в M3, с помощью MaterialTheme.typography:

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

Подробнее о рекомендациях Material Design по применению типографики…

Фигуры

Поверхности 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 */
}

Средняя форма для карточки и большая форма для плавающей кнопки действия в примере приложения Reply.
Рисунок 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

Surface в M3 – фоновая composable-функция, которая используется в большинстве компонентов M3, – поддерживает как тональное, так и теневое возвышение.

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

Компоненты Material Design

В 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 используется для компактных устройств, когда вы хотите настроить таргетинг на 5 или менее целевых страниц:

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 = { }
        )
    }
}

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

Используйте оба варианта в теме по умолчанию, чтобы обеспечить удобство работы на устройствах с экранами любого размера.

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

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

Демонстрация постоянной панели навигации в Reply
Рисунок 16. Демонстрация постоянной панели навигации в Reply

Навигация делает приложение удобнее и доступнее. Подробнее о компонентах навигации 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 Design предоставляет стандартные значения и измерения тонов, которые можно использовать для достижения доступных коэффициентов контрастности.

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

Все компоненты 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 для Display Small можно назначить разные значения в зависимости от контекста устройства, например телефона или планшета.

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

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

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

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

Подробнее…

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

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

Документы

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

Видео