Как отображать сообщения об ошибках для нескольких текстовых полей в jetpack compose

#android-jetpack #android-jetpack-compose

Вопрос:

Как отображать сообщения об ошибках для нескольких текстовых полей в jetpack compose. только с одним полем:

 private var isError by mutableStateOf(false)

private fun validate(text: String){
    isError = if(text.isEmpty()){
        true
    }else{
        android.util.Patterns.EMAIL_ADDRESS.matcher(text).matches()
    }

    Log.i("Boolean",isError.toString())

}

    TextField(value = email,placeholder = { Text(text = "E-mail")},
            onValueChange = {
                email=it
                isError = false
            },
            shape = RoundedCornerShape(8.dp),
            colors = TextFieldDefaults.textFieldColors(
                    focusedIndicatorColor = Color.Transparent,
                    unfocusedIndicatorColor = Color.Transparent,
                    disabledIndicatorColor = Color.Transparent

            ),
            singleLine = true,
            isError = isError,
            keyboardActions = KeyboardActions { validate(email) },
            modifier=Modifier.align(Alignment.CenterHorizontally),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
            leadingIcon = { Icon(imageVector = Icons.Default.Email, contentDescription = null) })
 

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

     var nome by rememberSaveable{ mutableStateOf("")}
    var email by rememberSaveable{ mutableStateOf("") }

       

 TextField(value = nome,placeholder = { Text(text = "Nome")},
                onValueChange = {
                    nome=it
                },
                shape = RoundedCornerShape(8.dp),
                colors = TextFieldDefaults.textFieldColors(
                        focusedIndicatorColor = Color.Transparent,
                        unfocusedIndicatorColor = Color.Transparent,
                        disabledIndicatorColor = Color.Transparent

                ),
                modifier=Modifier.align(Alignment.CenterHorizontally),
                leadingIcon = { Icon(imageVector = Icons.Default.Person, contentDescription = null) })

        Spacer(modifier = Modifier.padding(5.dp))


        TextField(value = email,placeholder = { Text(text = "E-mail")},
                onValueChange = {
                    email=it
                   
                },
                shape = RoundedCornerShape(8.dp),
                colors = TextFieldDefaults.textFieldColors(
                        focusedIndicatorColor = Color.Transparent,
                        unfocusedIndicatorColor = Color.Transparent,
                        disabledIndicatorColor = Color.Transparent

                ),
                modifier=Modifier.align(Alignment.CenterHorizontally),
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
                leadingIcon = { Icon(imageVector = Icons.Default.Email, contentDescription = null) })

    Spacer(modifier = Modifier.padding(5.dp))


    Button(
            onClick = { verifyEmpty(strings=validate) },
            colors = ButtonDefaults.buttonColors(
                    contentColor = colorResource(id = R.color.marron),
                    backgroundColor = colorResource (id = R.color.pastel_green)
            ),
    ) {
        Text(text = stringResource(id = R.string.view_cad),
                color= colorResource(id = R.color.marron))
    }
 

Ответ №1:

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

Я предлагаю вам создать класс состояния для вашего пользовательского текстового поля. Я сохраню текст, текст ошибки и логику валидатора. Таким образом, вы можете позвонить проверить, когда вам нужно: по нажатию кнопки или по кнопке готово на клавиатуре:

 @Composable
fun TestView(
) {
    val nomeState = rememberErrorTextFieldState("", validate = { text ->
        when {
            text.isEmpty() -> {
                "text.isEmpty()"
            }
            else -> null
        }
    })
    val emailState = rememberErrorTextFieldState("", validate = { text ->
        when {
            text.isEmpty() -> {
                "text.isEmpty()"
            }
            !android.util.Patterns.EMAIL_ADDRESS.matcher(text).matches() -> {
                "pattern doesn't match"
            }
            else -> null
        }
    })

    Column {
        ErrorTextField(
            state = nomeState,
            placeholderText = "nome",
            leadingIconVector = Icons.Default.Person,
            modifier = Modifier.align(Alignment.CenterHorizontally),
        )
        ErrorTextField(
            state = emailState,
            placeholderText = "email",
            leadingIconVector = Icons.Default.Email,
            modifier = Modifier.align(Alignment.CenterHorizontally),
        )
        Button(
            onClick = {
                listOf(nomeState, emailState).forEach(ErrorTextFieldState::validate)
            },
        ) {
            Text(text = "stringResource(id = R.string.view_cad)")
        }
    }
}


@Composable
fun ErrorTextField(
    state: ErrorTextFieldState,
    placeholderText: String,
    leadingIconVector: ImageVector,
    modifier: Modifier,
) {
    Column {
        val error = state.error
        TextField(
            value = state.text,
            onValueChange = { state.updateText(it) },
            placeholder = { Text(text = placeholderText) },
            shape = RoundedCornerShape(8.dp),
            colors = TextFieldDefaults.textFieldColors(
                focusedIndicatorColor = Color.Transparent,
                unfocusedIndicatorColor = Color.Transparent,
                disabledIndicatorColor = Color.Transparent,
                errorCursorColor = Color.Red
            ),
            singleLine = true,
            isError = error != null,
            leadingIcon = { Icon(imageVector = leadingIconVector, contentDescription = null) },
            keyboardActions = KeyboardActions {
                state.validate()
            },
            modifier = modifier,
        )
        if (error != null) {
            Text(
                error,
                color = Color.Red,
            )
        }
    }
}

@Composable
fun rememberErrorTextFieldState(
    initialText: String,
    validate: (String) -> String? = { null },
): ErrorTextFieldState {
    return rememberSaveable(saver = ErrorTextFieldState.Saver(validate)) {
        ErrorTextFieldState(initialText, validate)
    }
}

class ErrorTextFieldState(
    initialText: String,
    private val validator: (String) -> String?,
) {
    var text by mutableStateOf(initialText)
        private set

    var error by mutableStateOf<String?>(null)
        private set

    fun updateText(newValue: String) {
        text = newValue
        error = null
    }

    fun validate() {
        error = validator(text)
    }

    companion object {
        fun Saver(
            validate: (String) -> String?,
        ) = androidx.compose.runtime.saveable.Saver<ErrorTextFieldState, String>(
            save = { it.text },
            restore = { ErrorTextFieldState(it, validate) }
        )
    }
}
 

Комментарии:

1. Я последовал вашему примеру, но как проявляется ошибка? или просто установите для isError значение false или true? Я использую compose_version = ‘1.0.0-rc01’

2. Когда я нажимаю «Ввод» в текстовом поле, он выдает ошибку с этим кодом, если(ошибка){ Текст(текст = «сообщение об ошибке») } но как это работает при нажатии кнопки

3. @RafaelSouza Я пропустил, что вам нужна эта кнопка, обновил свой ответ. Также вы можете обновить до «1.0.1», он был выпущен некоторое время назад

4. Привет, Филип, функция сохранения выдает следующую ошибку : Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly

5. @StefanoSansone, Вероятно, с тех пор что-то изменилось =) Я обновил свой ответ