FormValidator
1.0.6indexedDeclarative form validation for Compose UI with Down/Up/Splash flows, ErrorSafe bundled state, live validation, configurable snackbar, and built-in rules (required, email, range, custom, optional).
Declarative form validation for Compose UI with Down/Up/Splash flows, ErrorSafe bundled state, live validation, configurable snackbar, and built-in rules (required, email, range, custom, optional).
A declarative form validation library for Kotlin Multiplatform + Jetpack Compose.
Targets Android, iOS, and Desktop (JVM).
**Documentation ** · Installation · Quick start · API reference
Down (stop at first error), (stop at last error), (all
fields at once)Available on Maven Central — no extra repository setup needed.
// build.gradle.kts
dependencies {
implementation("com.funyinkash:FormValidator:1.0.6")
}
dependencies {
implementation 'com.funyinkash:FormValidator:1.0.6'
}
| Target | Minimum |
|---|---|
| Android | API 21 |
| JVM (desktop) | JVM 8 |
| iOS | iosArm64, iosX64, iosSimulatorArm64 |
errorSafeerrorSafe bundles each field's value, validation error, and dirty-tracking into one remember
-able unit:
var firstName by remember { errorSafe("") }
var lastName by remember { errorSafe("") }
var email by remember { errorSafe("") }
FormValidatorval validator = remember(firstName.value, lastName.value, email.value) {
FormValidator(
flow = FormValidator.Flow.Down,
fields = buildList {
add(ValidationField(firstName.value, "First Name", FormValidator.Type.Required) {
firstName = firstName.copy(error = it)
})
add(ValidationField(lastName.value, "Last Name", FormValidator.Type.Required) {
lastName = lastName.copy(error = it)
})
add(ValidationField(email.value, "Email", FormValidator.Type.Email) {
email = email.copy(error = it)
})
}
)
}
Form and call validate()Form(validator) {
OutlinedTextField(
value = firstName.value,
onValueChange = { firstName = firstName.copy(value = it) },
label = { Text("First Name") },
isError = firstName.error != null,
supportingText = { firstName.error?.let { Text(it) } }
)
// ... remaining fields ...
Button(onClick = { if (validator.validate()) submit() }) {
Text("Submit")
}
}
LaunchedEffect(firstName.value, lastName.value, email.value) {
validator.validate()
}
val isDirty = firstName.modified || lastName.modified || email.modified
Button(enabled = isDirty, onClick = { if (validator.validate()) submit() }) {
Text("Save")
}
| Flow | Behaviour |
|---|---|
Down | Top-to-bottom; stops at the first failing field |
Up |
Form(
validator = validator,
snackBarProperties = SnackBarProperties(visibleDuration = 3000)
) {
// form content
Button(onClick = { validator.validate() }) { Text("Submit") }
}
ErrorSafeValue<T> propertiesFull documentation including guides, examples, and KDoc API reference:
https://funyin.github.io/FormValidator/
Copyright 2021 Funyinoluwa Kashimawo
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
UpSplashLaunchedEffect for as-you-type feedback| Type | Behaviour |
|---|
Required | Non-null; strings must also be non-blank |
Email | Validates against a standard email pattern |
MustBeMoreThan(n) | Numeric value must exceed n |
MustBeLessThan(n) | Numeric value must be below n |
MustBeEqualTo(target) | Value must equal target — useful for confirm-password |
MustBeInRange(min, max) | Numeric value must fall within [min, max] |
Custom { value -> "error" | null } | Fully custom predicate |
Optional | Always valid; participates in flow ordering without blocking |
| Bottom-to-top; stops at the first failing field |
Splash | All fields at once; every failing field gets an error |
| Property | Type | Description |
|---|
value | T | Current field value |
initial | T | Value at construction time |
error | String? | Current validation error, null when valid |
modified | Boolean | true when value != initial |
Surfaced from shared tags and platforms — no rankings paid for.