A declarative form validation library for Kotlin Multiplatform + Jetpack Compose.
Targets Android, iOS, and Desktop (JVM).
**Documentation ** · Installation · Quick start · API reference
device-2022-01-15-014026.mp4
- Validation flow —
Down(stop at first error),Up(stop at last error),Splash(all fields at once) - Validation types — Required, Email, range / equality checks, fully Custom, Optional
- ErrorSafe — single state object per field bundles value + error + dirty-tracking
- Validation snackbar — built-in auto-dismissing error snackbar with configurable appearance
- Live validation — designed to work with
LaunchedEffectfor as-you-type feedback
Available on Maven Central — no extra repository setup needed.
// build.gradle.kts
dependencies {
implementation("com.funyinkash:FormValidator:1.0.6")
}Groovy DSL
dependencies {
implementation 'com.funyinkash:FormValidator:1.0.6'
}| Target | Minimum |
|---|---|
| Android | API 21 |
| JVM (desktop) | JVM 8 |
| iOS | iosArm64, iosX64, iosSimulatorArm64 |
errorSafe 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("") }val 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(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")
}| 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 |
| Flow | Behaviour |
|---|---|
Down |
Top-to-bottom; stops at the first failing field |
Up |
Bottom-to-top; stops at the first failing field |
Splash |
All fields at once; every failing field gets an error |
Form(
validator = validator,
snackBarProperties = SnackBarProperties(visibleDuration = 3000)
) {
// form content
Button(onClick = { validator.validate() }) { Text("Submit") }
}| 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 |
Full 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
