Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Введение

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

Сначала быстрая подготовка. Как видите, у меня в уже стандартной для предыдущих уроков обертке вызвана одна единственная функция StudyAppHeader().

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Однако, ее тело находится в другом файле.

  • Во-первых так можно делать, все хорошо.
  • Во-вторых я добавлю параметры заголовка и подзаголовка в функцию, чтобы переиспользовать ее с любыми другими строками. Сохраняя при этом единый стиль. Тоже хорошая практика, чтобы функции были универсальными и переиспользуемыми. А также благодаря передаче данных через. параметры можно выносить бизнес-логику в отдельные функции или еще лучше во ViewModel.
Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

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

@Composable
@Preview
fun PrimaryButton() {
    Button(
        shape = RoundedCornerShape(13.dp),
        onClick = {},
        modifier = Modifier
            .height(56.dp)
            .padding(40.dp, 0.dp)
            .fillMaxWidth()
    ) {
        Text(
            "Зарегистрироваться",
            style = MaterialTheme.typography.labelMedium
        )

    }
}

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

В XML верстке мы использовали компонент EditText. В Jetpack Compose такого компонента нет. Здесь используется TextField — аналогичный компонент, который выполняет ту же функцию, что и EditText. Он позволяет вводить и редактировать текст.

Если ввести TextField, то можем увидеть и другие компоненты, помеченные значком Compose.

  • OutlinedTextField имеет тот же функционал, что и TextField, но поле отображается с обводкой (или с рамкой).
  • BasicTextField – более низкоуровневый компонент для текстового ввода, который дает больше контроля над стилизацией и поведением. Используется в основном для создания собственных текстовых полей.
Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Создание текстового поля OutlinedTextField

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

  • value – это поле, которое должно отображать содержимое этого самого текстового поля. Не путать с плейсхолдером. Мы можем вписать сюда какой-нибудь текст и он будет отображаться в поле так, как будто его уже вписали заранее. Для примера что-то напишу сюда.
  • onValueChange – работает точно также, как и onCheckedChange на предыдущем уроке. Только там при клике в лямбду передавалось значение. Здесь же при каждом изменении текстового поля эта лямбда будет обновляться. Неважно вводим мы какой-то символ или удаляем.
  • shape – скопирую кнопки, это добавит единого стиля.
  • textStyle – добавлю по той же причине из переопределенных нами ранее стилей.

Вызовем функцию в setContent и посмотрим что получилось. Обратите внимание, из поля value строка отобразилась в текстовом поле. Курсор активируется, но ничего напечатать или стереть невозможно.

@Composable
@Preview(showBackground = true)
fun CheckEmailFields() {
    OutlinedTextField(
        value = "42 42 42",
        onValueChange = {},
        shape = RoundedCornerShape(13.dp),
        textStyle = MaterialTheme.typography.headlineMedium,
    )
}

Причина все та же, что и с чекбоксом. Compose не вызывает рекомпозицию, потому, что не знает в какой момент это нужно делать, да и не написан требуемый инструментарий. Воспользуемся аналогичным объектом MutableState в связке с remember. Импортируем делегат by и значение по умолчанию перенесу из value в стейт. И не забываем в лямбде при изменении текстового поля присваивать значения нашему стейту. Теперь можно проверять.

@Composable
@Preview(showBackground = true)
fun CheckEmailFields() {

    var textState by remember { mutableStateOf("") }

    OutlinedTextField(
        value = textState,
        onValueChange = {
            textState = it
        },
        shape = RoundedCornerShape(13.dp),
        textStyle = MaterialTheme.typography.headlineMedium,
	  )
}

Добавление Placeholder

Отлично. Стейт подхватывает изменения при вводе каждого символа и вызывает рекомпозицию. Теперь уберу значение по умолчанию и добавлю плейсхолдер с помощью специализированного для OutlinedTextField параметра функции. Placeholder последним параметром принимает composable лямбду, поэтому строка там устанавливается обычной функцией Text с оформлением по нашим предпочтениям. Добавим стиля и цвета.

Добавление SingleLine

Работает хорошо, но если я буду нажимать enter, то по умолчанию текстовое поле будет увеличиваться.

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Нам такое поведение не нужно, поэтому добавлю параметр singleLine = true. По умолчанию он установлен в положение false.

Добавление Label

Есть еще один крутой атрибут для стилизации – label. Точно также задаем какой-то текст внутри. На стиль не обращайте внимания, я его переопределил по собственным размерам. В итоге мы получаем вот такую анимацию лейбла при активации текстового поля. Ну, это красиво.

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Добавление иконки с кнопкой в текстовое поле – IconButton

Следующая пара параметров отвечает за иконки внутри текстового поля. Из них можно сделать кнопки с каким-нибудь функционалом.

  • leadingIcon добавит иконку в начало поля,
  • trailingIcon добавляет в конец.

Этот параметр принимает composable функцию, следовательно в нее отправляем то, что хотим отобразить. Пусть это будет IconButton – смотрим в декларацию, у этой функции первый параметр onClick обязательный. То есть что будет происходить при нажатии – оставим пока пустую лямбду.

Ну а чтобы добавить само изображение, нужно последним параметром IconButton – то есть в лямбде вызвать функцию Icon. Здесь принцип схож с добавлением картинки. Первым параметром задается сама иконка, взять ее можно из стандартного набора предоставляемого Material: imageVector = Icons.Filled.Clear. Второй параметр contentDescription тоже обязательный – добавим описание иконки. Готово.

Смотрим. Иконка отрисовалась, она кликабельна, все хорошо.

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

И мы можем задать ей какое-то полезное действие очень просто. Пусть она очищает введеный текст.

Как это сделать? Во-первых обратить внимание на пустую лямбду – здесь должно что-то происходить по клику на иконку. Во-вторых обратить внимание на изменяемую переменную со стейтом, которая при изменении вызывает рекомпозицию. Следовательно здесь достаточно просто задавать стейту пустую строку. И она сразу же отрисуется в TextField. Проверяем – и все происходит именно так. Великолепно.

trailingIcon = {
    IconButton(
        onClick = { textState = "" }
    ) {
        Icon(
            imageVector = Icons.Filled.Clear,
            contentDescription = "Иконка очистки поля"
        )
    }
}

Валидация email и ErrorState

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

Для начала воспользуемся у нашей composable функции параметром isError. Если его установить в true и посмотреть что будет – обнаружим текстовое поле в состоянии “Ошибка”. Оно окрашено в красный.

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

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

Как правило в Compose все логично, то есть как слышится, так и пишется. Когда я говорил про состояние “Ошибка” – я буквально имел в виду, что нам нужно создать дополнительный стейт для хранения состояния ошибки. Сделаем это.

var errorState by remember { mutableStateOf("") }

Мы будем хранить строку, хотя в стейте можно хранить что угодно. Например, Boolean – флаг есть ошибка или нет, код конкретной полученной ошибки или целый объект, предварительно создав для него класс. То же самое относится и к обычным стейтам, по сути они ничем не отличаются, мы просто решили, что этот объект MutableState будет отвечать за хранение ошибок в данной функции.

Теперь самое интересное. Где валидировать введенный текст? Конечно, в onValueChange, после каждого введенного символа. Так что textState мы не трогаем, чтобы текст отрисовывался как положено. А далее присваиваем errorState следующий код.

errorState = if (EMAIL_ADDRESS.matcher(it).matches()) "" else "Некорректный email"

Код проверяет, соответствует ли введённый текст формату электронной почты с помощью регулярного выражения. Если текст правильный, то errorState устанавливается в пустую строку (что будет означать отсутствие ошибок), а если неправильный — присваивается сообщение «Введите корректный email», чтобы уведомить пользователя о необходимости исправить ввод.

Наконец, сейчас у нас isError захардкожен. Добавим запись о том, что показывать ошибку только если стейт с ошибкой не пустой (то есть не является пустой строкой) – isError = errorState.isNotEmpty(),.

Запускаем. Вот что получается. При вводе каждого символа происходит валидация строки и пока она не похожа на почту – в стейт записывается “Введите корректный email”.

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Как только я добавляю в конце .ru – ошибка пропадает.

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

label = {
    Text(
        text = if (errorState.isEmpty()) "Электропочта" else errorState,
        style = MaterialTheme.typography.headlineSmall,
    )
},
Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState
Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Последний штрих – очищать errorState при клике на кнопку очистки поля, иначе сейчас очищается только стейт с текстом, а ошибка остается.

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

Добавлю errorState = "".

Выглядит довольно неплохо.

Урок 9: TextField /OutlinedTextField, Валидация почты, ErrorState

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

Бесплатные 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.
Узнать подробнее