Skip to content

Examples


Validating a login form

@Composable
fun LoginForm(onLogin: (email: String, password: String) -> Unit) {
    var email    by remember { errorSafe("") }
    var password by remember { errorSafe("") }

    val validator = remember(email.value, password.value) {
        FormValidator(
            flow = FormValidator.Flow.Down,
            fields = listOf(
                ValidationField(email.value, "Email", FormValidator.Type.Email) {
                    email = email.copy(error = it)
                },
                ValidationField(password.value, "Password", FormValidator.Type.Required) {
                    password = password.copy(error = it)
                }
            )
        )
    }

    Form(validator) {
        OutlinedTextField(
            value = email.value,
            onValueChange = { email = email.copy(value = it) },
            label = { Text("Email") },
            isError = email.error != null,
            supportingText = { email.error?.let { Text(it) } }
        )
        OutlinedTextField(
            value = password.value,
            onValueChange = { password = password.copy(value = it) },
            label = { Text("Password") },
            visualTransformation = PasswordVisualTransformation(),
            isError = password.error != null,
            supportingText = { password.error?.let { Text(it) } }
        )
        Button(onClick = {
            if (validator.validate()) onLogin(email.value, password.value)
        }) { Text("Log in") }
    }
}

Matching a confirm-password field

var password        by remember { errorSafe("") }
var confirmPassword by remember { errorSafe("") }

val validator = remember(password.value, confirmPassword.value) {
    FormValidator(
        flow = FormValidator.Flow.Down,
        fields = listOf(
            ValidationField(password.value, "Password", FormValidator.Type.Required) {
                password = password.copy(error = it)
            },
            ValidationField(
                value = confirmPassword.value,
                name = "Confirm Password",
                type = FormValidator.Type.MustBeEqualTo(target = password.value)
            ) { confirmPassword = confirmPassword.copy(error = it) }
        )
    )
}

Validating a numeric range

var seats by remember { errorSafe(1) }

val validator = remember(seats.value) {
    FormValidator(
        flow = FormValidator.Flow.Splash,
        fields = listOf(
            ValidationField(
                value = seats.value,
                name = "Seats",
                type = FormValidator.Type.MustBeInRange(min = 1, max = 10)
            ) { seats = seats.copy(error = it) }
        )
    )
}

Using a custom rule

var username by remember { errorSafe("") }

val validator = remember(username.value) {
    FormValidator(
        flow = FormValidator.Flow.Down,
        fields = listOf(
            ValidationField(
                value = username.value,
                name = "Username",
                type = FormValidator.Type.Custom { value ->
                    when {
                        value.length < 3  -> "At least 3 characters required"
                        value.contains(" ") -> "Spaces are not allowed"
                        else -> null
                    }
                }
            ) { username = username.copy(error = it) }
        )
    )
}

Showing a snackbar on validation failure

Form(
    validator = validator,
    snackBarProperties = SnackBarProperties(visibleDuration = 3000)
) {
    // fields ...
    Button(onClick = { validator.validate() }) { Text("Submit") }
}

Validating all fields simultaneously with Splash flow

Flow.Splash reports errors on every failing field at once instead of stopping at the first one:

val validator = FormValidator(
    flow = FormValidator.Flow.Splash,
    fields = listOf(firstNameField, lastNameField, emailField)
)

Gating submit on dirty state

var name  by remember { errorSafe(profile.name) }
var email by remember { errorSafe(profile.email) }

val hasChanges = name.modified || email.modified

Button(
    enabled = hasChanges,
    onClick = { if (validator.validate()) save() }
) { Text("Save changes") }