Jetpack Compose строит Android-интерфейс из Kotlin-функций: состояние меняется, а нужная часть экрана пересчитывается автоматически. Создадим Empty Activity, добавим поле имени, кнопку и приветствие. Проект заработает в Preview и на эмуляторе, а rememberSaveable сохранит введённые данные при повороте экрана.
Первый Android-проект пугает количеством файлов, хотя пользовательский сценарий маленький. Activity является точкой входа, setContent запускает Compose, а composable-функция описывает текущий вид экрана. Поле ввода не изменяет Text напрямую: оно обновляет переменную state. Compose замечает изменение и выполняет recomposition.
Empty Activity готовит Kotlin, Compose, тему и конфигурацию сборки.
rememberSaveable держит имя и приветствие между recomposition и поворотом.
Preview показывает компоновку быстро, эмулятор подтверждает ввод, клавиатуру и lifecycle.
Как Activity, Compose и state связаны между собой
Android Studio создаёт проект из Empty Activity. В onCreate вызывается setContent, внутри темы запускается GreetingScreen. Два состояния хранят введённое name и готовую строку greeting. OutlinedTextField получает value и onValueChange, поэтому является контролируемым полем. Button по клику очищает пробелы и формирует приветствие или подсказку.
| Этап | Что происходит | Признак результата |
|---|---|---|
| Activity | onCreate запускает setContent | Compose tree создан |
| State | Поле меняет name через onValueChange | Переменная обновлена |
| Recomposition | Compose перечитывает затронутые composable | UI показывает новое значение |
| Device | Эмулятор запускает APK и lifecycle | Поворот сохраняет state |
У проекта есть один главный маршрут: получить входные данные, проверить их, выполнить действие и показать результат. Если каждый этап можно проверить отдельно, ошибка перестаёт быть загадкой.

От входных данных до видимого результата. Событие меняет данные, Compose приводит UI в соответствие
Собираем экран с полем и кнопкой
Установите актуальную Android Studio, выберите New Project, затем Empty Activity, Kotlin и поддерживаемый minimum SDK. Дождитесь Gradle Sync. Откройте MainActivity.kt и замените содержимое экрана на пример ниже, сохранив package и тему своего проекта. Preview можно запустить из редактора, а для устройства создайте эмулятор в Device Manager.
@Composable
fun GreetingScreen(modifier: Modifier = Modifier) {
var name by rememberSaveable { mutableStateOf("") }
var greeting by rememberSaveable { mutableStateOf("Введите имя") }
Column(
modifier = modifier
.fillMaxSize()
.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Первое приложение", style = MaterialTheme.typography.headlineMedium)
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Ваше имя") },
singleLine = true
)
Button(onClick = {
val cleanName = name.trim()
greeting = if (cleanName.isEmpty()) "Сначала введите имя" else "Привет, $cleanName!"
}) {
Text("Поздороваться")
}
Text(greeting, style = MaterialTheme.typography.titleLarge)
}
}- Preview показывает поле, кнопку и начальную подсказку без запуска эмулятора.
- Ввод имени сразу обновляет state поля, но приветствие меняется только по кнопке.
- Пустое имя после trim даёт понятное сообщение.
- После поворота эмулятора name и greeting сохраняются благодаря rememberSaveable.
Экран является функцией от состояния. Мы не ищем Text по id и не присваиваем ему значение вручную. Меняется greeting, после чего Compose приводит интерфейс в соответствие.

Что отличает устойчивый проект от случайного успеха. Один источник состояния упрощает объяснение экрана
Почему интерфейс перерисовывается без ручного поиска View
mutableStateOf создаёт наблюдаемое состояние. Когда composable читает name или greeting, Compose запоминает зависимость. Изменение значения планирует recomposition затронутой части дерева. remember удерживает значение между recomposition, но может потерять его при пересоздании Activity.
| Часть | Ответственность | Что проверить |
|---|---|---|
| ComponentActivity | Входит в Android lifecycle | setContent вызывается один раз |
| Composable | Описывает экран для текущего state | Не меняет View вручную |
| mutableStateOf | Хранит наблюдаемое значение | Изменение вызывает recomposition |
| rememberSaveable | Сохраняет простое UI-state | Поворот не очищает поле |
| Preview | Показывает функцию в редакторе | Не заменяет тест на устройстве |
Recomposition не обязана выполнять весь экран и не означает полную перерисовку пикселей. Compose может пропускать части, входы которых не изменились. Поэтому composable должна быть быстрой и без неожиданных побочных эффектов. Не запускайте сетевой запрос прямо в теле функции: при recomposition он повторится.
Проверяем Preview, поворот и пустой ввод
Запустите Preview и измените текст заголовка. Затем установите приложение на эмулятор, введите имя и поверните экран. Убедитесь, что оба значения сохранились. Замените rememberSaveable на remember, повторите поворот и сравните. Верните saveable. Добавьте счётчик приветствий как Int и вывод «Нажатий: N». После этого ограничьте имя 30 символами в onValueChange.
- Создайте Empty Activity и дождитесь успешного Gradle Sync.
- Покажите GreetingScreen в Preview.
- Запустите эмулятор и проверьте пустое и обычное имя.
- Сравните remember и rememberSaveable при повороте.
- Добавьте счётчик кликов с типом Int.
- Проверьте экран в светлой и тёмной системной теме.
- Проект собирается из текущего шаблона.
- Preview и эмулятор показывают один экран.
- Пустой ввод обработан.
- Состояние переживает поворот.
Первое приложение готово, когда вы можете предсказать, какая переменная изменится после каждого события и почему экран обновится. Кнопка и красивый Preview без понимания state дают слишком хрупкую основу.

Четыре проверки перед следующим шагом. Preview и эмулятор отвечают на разные вопросы
Готовим приложение к следующему экрану
Дальше вынесите state и обработчики в параметры GreetingScreen, чтобы composable было проще тестировать. Затем добавьте ViewModel и разделите UI-state от событий. Для второго экрана используйте Navigation Compose только после того, как первый экран не хранит бизнес-логику внутри кнопки.
Как изучить тему в Кодике
В Кодике сначала закрепите переменные, функции и классы Kotlin. Затем напишите чистую функцию, которая принимает name и возвращает greeting. Только после этого подключите её к state и кнопке Compose.
| Шаг | Что изучить в Кодике | Мини-проверка |
|---|---|---|
| 1 | Строки и функции Kotlin | Сформировать greeting из cleanName |
| 2 | Условия и null safety | Обработать пустой ввод |
| 3 | Composable и Modifier | Собрать Column с отступами |
| 4 | State и события | Связать value и onValueChange |
| 5 | Lifecycle UI-state | Сравнить remember и rememberSaveable |
Не пытайтесь запомнить весь API. В тренажёре Кодика сначала воспроизведите чистую логику без библиотеки, затем восстановите подключение и обработку ошибок. Финальный тест: объяснить проект по памяти и добавить одну свою функцию.
Используйте актуальный Empty Activity и меняйте экран, не ломая согласованные версии.
Recomposition может повторить вызов. Используйте архитектурный слой и API эффектов.
UI должен читаться из state, а события изменяют state, не отдельные View.
Запустите эмулятор для клавиатуры, поворота и lifecycle.
Что получится в итоге

Первый Android-экран принимает имя, обновляет state по кнопке, переживает поворот и одинаково выглядит в Preview и на эмуляторе.
Что запускает recomposition?
Изменение наблюдаемого state, который был прочитан composable-функцией.
Чем rememberSaveable отличается от remember?
Он может восстановить поддерживаемое простое значение после пересоздания Activity, например при повороте.
Зачем нужен Preview, если есть эмулятор?
Preview ускоряет визуальную работу, а эмулятор проверяет lifecycle, ввод и поведение реального Android.
Как понять, что проект действительно работает?
Проверьте основной сценарий, неверный ввод, повторный запуск и один граничный случай. Затем объясните путь данных своими словами.
Можно ли начать с готового кода из статьи?
Да. Добейтесь результата, измените одно правило и соберите ключевой файл заново без копирования.
Функции, классы и условия закрепите в курсе Java и Kotlin. Ошибки объектов сравните с материалом про NullPointerException.
Проект сохраните через GitHub, а идею быстрого мобильного прототипа сравните со статьёй про мобильное приложение с ИИ.