Basic screen
When to use: quick forms and screens with no state holder — the validators live directly in the composable.
@Composable
fun BasicSignUpScreen(onSubmit: (email: String, password: String) -> Unit) {
// No state holder: the validators live in the composable, remembered across
// recompositions. Each one owns its TextFieldValue draft + validation state.
val email = rememberTextFieldValueValidator(rules = listOf(Required, Email))
val password = rememberTextFieldValueValidator(rules = listOf(Required, MinLength(8)))
Column {
with(email) {
OutlinedTextField(
value = value,
onValueChange = ::onValueChange,
label = { Text("Email") },
isError = isError(),
supportingText = supportingText(),
singleLine = true,
modifier = Modifier
.fillMaxWidth()
// Show errors when focus leaves the field; shake on invalid submit.
.validationConfig(validateOnFocusLost = true, shakeOnInvalid = true),
)
}
with(password) {
OutlinedTextField(
value = value,
onValueChange = ::onValueChange,
label = { Text("Password") },
isError = isError(),
supportingText = supportingText(),
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.validationConfig(validateOnFocusLost = true, shakeOnInvalid = true),
)
}
Button(
onClick = {
// Form-level validate(): surfaces errors on every field, true when all pass.
if (listOf(email, password).validate()) {
onSubmit(email.value.text, password.value.text)
}
},
modifier = Modifier.fillMaxWidth(),
) { Text("Create account") }
}
}
How the wiring works:
rememberTextFieldValueValidatorkeeps each validator across recompositions. The validator owns the field'sTextFieldValuedraft and its validation state — no separatemutableStateOfneeded.Modifier.validationConfig(validateOnFocusLost = true, shakeOnInvalid = true)starts validation when the user leaves the field and shakes it when a submit finds it invalid. Nothing turns red while they're still typing.- The submit button gates on
listOf(email, password).validate()— this surfaces errors on every field (including ones the user never touched) and returnstrueonly when all rules pass.
When the form grows a real state holder, graduate to the ViewModel, MVI, or Circuit wiring.