Урок 7: Темная тема и стили

Урок 7: Темная тема и стили

Введение

В этом уроке рассмотрим работу со стилями и темами в Jetpack Compose. Скорее всего те, кто уже имеет насмотренность, мог заметить, что я часто дублирую модификаторы оформления, например, для текстов. Все это принято выносить в стили.

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

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

Как устроены темы в Jetpack Compose

Темы настраиваются в файле ui.theme.Theme.kt соответственно. Что тут есть:

Сначала прописаны две переменные с присвоенными функциями объявления цветов для светлой и темной тем.

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

Первый параметр функции darkTheme определяет включена ли сейчас на устройстве темная тема – значение автоматически берется из системы.

Второй параметр dynamicColor управляет тем, будет ли использоваться динамическое цветовое оформление в приложении. Динамическое цветовое оформление (Dynamic Color) позволяет приложениям использовать цвета, основанные на фоне или теме устройства. Это актуально для Android 12 и выше, где пользователи могут устанавливать свои темы и цвета для приложения.

Ну и третий параметр content уже нам знаком, он ожидает какую-то composable функцию. А это значит, что внутри этой функции с темой мы будем размещать содержимое экрана.

Если помните, при создании нового проекта эта функция уже была прописана в методе setContent. Зачем это было нужно? Дело в том, что размещая контент экрана внутри функции темы – мы можем использовать цвета из этой темы.

Внутри прописана очень простая логика. colorScheme берет такую цветовую схему, в зависимости от ветвления when. Либо это динамический цвет (при условии подходящей версии Android), либо темная (флаг получаем от системы), иначе светлая. Но в моем случае динамический цвет я закомментирую, так как не хочу непредсказуемого поведения в моем приложении. Только темная и светлая палитры.

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

Настройка темной темы

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

Считывать их можно так:

  • primary Основной цвет приложения. Обычно используется для главных элементов интерфейса, таких как кнопки или выделенные блоки.
  • secondary Второстепенный цвет. Применяется для элементов, которые менее акцентированы, например:
    • Вторичные кнопки.
    • Активные элементы в секциях.
    • Дополнительные акценты.
  • tertiary Третичный (дополнительный) цвет. Часто используется для небольших декоративных элементов:
    • Иконки.
    • Линии выделения.
    • Дополнительные маркеры или индикаторы.
  • surface – можно использовать в карточках, модальных окнах, попапах и так далее.
  • background – задает фон соответственно, тут все просто.
  • Цвета с префиксом on определяют, какой цвет текста или иконок будет использоваться на соответствующих фонах.

Переопределение цветов

Для начала добавим недостающие цвета в файл с палитрой. У меня их немного. Для светлой темы обозначу бэкграунд, так как он не чисто белого цвета. Также будет почти черный заголовок и серый подзаголовок.

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

Настройка фона

Возвращаемся в файл с темой. Теперь будем переопределять цвета для светлой и темной темы. Самое простое – это разобраться с фоном. Пропишу для background и для всплывающих поверхностей surface темные и светлые фоны. Для темной темы с пометкой Dark соответственно.

Можем уже проверить результат. Самое главное не забыть все функции в setContent обернуть в функцию темы нашего текущего приложения. Теперь можно переключить темную тему и собрать проект.

Настройка заголовков

Далее заголовки. Буду полагать, что это основной цвет текста в приложении, поэтому переопределяю onBackground для светлой и темной темы соответственно. Чтобы было наглядней видно я пока поставлю более яркий цвет (0xFFFF00B4). Сморите, Material определил onBackground и применил цвет текста автоматически. Он будет его подтягивать для обеих тем, поэтому можно смело переопределять на наши цвета.

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

Окей. До этого моменты мы вообще не лезли в Activity, но может возникнуть ситуация, когда конкретный цвет нужно применить к определенному элементу. Например, к тексту на кнопке. Укажем его явно, обратившись к объекту MaterialTheme. color = MaterialTheme.colorScheme.onBackground. И так можно делать для любого элемента, причем цвет будет автоматически меняться в зависимости от темы устройства.

Настройка подзаголовков – кастомная composable функция

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

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

val ColorScheme.subtitle: Color
    @Composable
    get() = if (isSystemInDarkTheme()) subtitleGrayColorDark else subtitleGrayColor

Теперь все, что остается – это обратиться к этому свойству при установке цвета в функции с текстом.

Text(
    text = "Изучение Kotlin & Android через практику",
    fontFamily = FontFamily(Font(R.font.gilroy_medium)),
    fontSize = 16.sp,
    color = MaterialTheme.colorScheme.subtitle,
)

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

Preview для светлой и темной темы

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

Теперь если вернуться к функции темы приложения увидим вторым по счету Boolean параметр darkTheme. Он, как мы помним, отвечает за то, включена ли сейчас темная тема или нет. И мы можем передавать его принудительно.

Для этого в превью функции вызываем функцию темы приложения с соответствующими параметрами true или false. А внутри необходимые нам функции с элементами. Вот и все, теперь на превью видны актуальные для нас варианты.

@Composable
@Preview(showBackground = true)
private fun MainNavButtonsPreviewLight() {
    ComposePreviewTheme(
        darkTheme = false
    ) {
        MainNavButtons()
    }
}

@Composable
@Preview(showBackground = true)
private fun MainNavButtonsPreviewDark() {
    ComposePreviewTheme(
        darkTheme = true
    ) {
        MainNavButtons()
    }
}

Стили в Jetpack Compose

Далее. Как я уже говорил в начале – множество модификаторов текста у нас повторяются и их можно объединить в стили. Настройка оформления текста происходит в файле Type.

Этот файл отвечает за настройку типографики (класс Typography). Типографика определяет стили текста, такие как шрифт, его размер, вес, межстрочное расстояние и межбуквенный интервал.

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

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

Поэтому что я сделаю:

  • Для заголовка переопределю headlineLarge и перенесу сюда шрифт и размер;
  • Для подзаголовка переопределю headlineMedium и также перенесу значения.

Теперь просто буду использовать значения этих параметров в параметре style composable функций. Для заголовка и двух подзаголовков.

style = MaterialTheme.typography.headlineLarge
style = MaterialTheme.typography.headlineMedium

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

В целом я который раз повторю, что верстать на Jetpack Compose одно удовольствие. Но и про базу забывать нельзя, так как много проектов на рынке еще используют XML подход.

Бесплатные Telegram-боты для обучения

Практика с проверкой кода и помощью ИИ-ментора

AndroidSprint AI Mentor

Проверяет Pull Request'ы в GitHub, проводит тестовые собеседования с голосом и таймером, помогает разбираться с кодом 24/7

Попробовать ИИ-ментора →

KotlinSprint Bot

22 урока Kotlin, 220 тестов, 120 практических задач с код-ревью

Начать обучение Kotlin →

AndroidSprint Bot

Тесты по Android SDK, Jetpack Compose, архитектуре приложений

Тесты по Android →

Тебе также может быть интересно

Узнать подробнее
Курс AndroidSprint

Глубокое обучение Android разработке с 0 до получения оффера. Только персональная практика с гарантией получения продуктового опыта.

Курс AndroidSprint - Глубокое <strong>обучение Android разработке с 0 до получения оффера</strong>. Только персональная практика с гарантией получения продуктового опыта.
Узнать подробнее
Узнать подробнее
Практикум по Kotlin

Изучение Котлин с 0 для профессиональной разработки. Личный ментор и разбор кода задач через git-flow.

Практикум по Kotlin - Изучение Котлин <strong>с 0 для профессиональной разработки</strong>. Личный ментор и разбор кода задач через git-flow.
Узнать подробнее
Узнать подробнее
Бесплатные уроки по Kotlin разработке

Самостоятельное освоение базы по языку для дальнейшего развития в Android/back-end разработке или в автотестах.

Бесплатные уроки по Kotlin разработке - <span>Самостоятельное освоение базы по языку для дальнейшего развития в Android/back-end разработке или в автотестах.</span>
Узнать подробнее
Узнать подробнее
Onboarding в разработку

Полное обучение Android разработке с нуля до получения оффера. Делаем упор на практику и обратную связь

Onboarding в разработку - <span>Полное обучение Android разработке с нуля до получения оффера. Делаем упор на практику и обратную связь</span>
Узнать подробнее
Узнать подробнее
Обучающий Kotlin телеграм бот (с тестами)

Ваш основной инструмент для изучения основ языка. Бесплатные тесты и практика внутри.

Обучающий Kotlin телеграм бот (с тестами) - Ваш основной <span>инструмент для изучения основ языка.</span> Бесплатные тесты и практика внутри.
Узнать подробнее
Узнать подробнее
Бесплатные уроки по Android разработке

Самостоятельное обучение разработке Андроид приложений. Понятные видеоуроки с разжеванными примерами.

Бесплатные уроки  по Android разработке - Самостоятельное <span>обучение разработке Андроид приложений.</span> Понятные видеоуроки с разжеванными примерами.
Узнать подробнее
Узнать подробнее
Курс по UI/Unit тестированию

Для ручных тестировщиков, которые готовы осваивать автотесты с использованием актуального стека технологий. [в разработке]

Курс по UI/Unit тестированию - Для ручных тестировщиков, которые <span>готовы осваивать автотесты</span> с использованием актуального стека технологий. [в разработке]
Узнать подробнее
Узнать подробнее
Обучающий Android телеграм бот (с тестами)

Бесплатные теоретические тесты для самопроверки. А также информер на практических спринтах по Android.

Обучающий Android телеграм бот (с тестами) - Бесплатные <span>теоретические тесты для самопроверки.</span> А также информер на практических спринтах по Android.
Узнать подробнее