Skip to content

Latest commit

 

History

122 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Formidable

Formidable — Headless forms for Jetpack Compose

Maven Central CI codecov License: MIT Kotlin Kotlin Multiplatform Compose Multiplatform

Formidable generates type-safe form controllers from annotated Kotlin data classes. Define your form once — get state management, validation, and Compose integration on Android, iOS, and Web.

Formidable demo


Quick Start

Three steps. That's it.

1. Annotate a data class

@FormSchema
data class LoginForm(
    @Field(label = "Email")
    @Email
    val email: String = "",

    @Field(label = "Password")
    @MinLength(8)
    val password: String = "",
)

2. Use the generated controller

KSP generates LoginFormController — no boilerplate, no reflection.

class LoginViewModel : ViewModel() {
    val controller = LoginFormController()
}

3. Build your UI

@Composable
fun LoginScreen(viewModel: LoginViewModel) {
    val emailState by viewModel.controller.email.collectAsState()
    val passwordState by viewModel.controller.password.collectAsState()
    val isValid by viewModel.controller.isValid.collectAsState()

    Formidable {
        StringField(
            state = emailState,
            onValueChange = { viewModel.controller.updateEmail(it) },
            onFocusLost = { viewModel.controller.touchEmail() },
        )
        StringField(
            state = passwordState,
            onValueChange = { viewModel.controller.updatePassword(it) },
            onFocusLost = { viewModel.controller.touchPassword() },
            config = {
                visualTransformation = PasswordVisualTransformation()
                keyboardType = KeyboardType.Password
            },
        )
        Button(onClick = { /* submit */ }, enabled = isValid) {
            Text("Login")
        }
    }
}

Formidable renders OutlinedTextField for you, auto-wires focus, keyboard navigation, and error display. Need a different style or a password field? Use config = { ... }. Need full control? Pass a trailing lambda instead.


Installation

Android (Jetpack Compose)

// build.gradle.kts
plugins {
    id("com.google.devtools.ksp") version "2.2.20-2.0.3"
}

dependencies {
    implementation("io.github.wassimbeltaief:formidable-core:2.1.7")
    implementation("io.github.wassimbeltaief:formidable-compose:2.1.7")
    ksp("io.github.wassimbeltaief:formidable-ksp:2.1.7")
}

Kotlin Multiplatform (Compose Multiplatform)

KSP in KMP runs against commonMain metadata, which requires a small amount of extra wiring to make the generated sources visible to all targets.

// build.gradle.kts
plugins {
    id("com.google.devtools.ksp") version "2.2.20-2.0.3"
}

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("io.github.wassimbeltaief:formidable-core:2.1.7")
            implementation("io.github.wassimbeltaief:formidable-compose:2.1.7")
        }
    }
}

dependencies {
    // KSP processes @FormSchema in commonMain, generates a Controller for all targets
    add("kspCommonMainMetadata", "io.github.wassimbeltaief:formidable-ksp:2.1.7")
}

// Ensure KSP metadata runs before any compilation task
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>().configureEach {
    if (!name.startsWith("ksp")) {
        dependsOn("kspCommonMainKotlinMetadata")
    }
}

// Make generated sources visible in commonMain
kotlin.sourceSets.commonMain {
    kotlin.srcDir(layout.buildDirectory.dir("generated/ksp/metadata/commonMain/kotlin"))
}

Try it live

wassimbeltaief.github.io/Formidable — interactive demo running in the browser


Documentation

In-Depth Guide Schema → controller, rendering modes, FieldScope, all field types with examples, validation reference
GitHub Pages Interactive docs + live demo
Sample App Full sign-up form: async validation, cross-field rules, conditional fields, enum dropdown
Changelog Release history
Contributing How to contribute

Roadmap

Item Description
Localization / i18n Structured ValidationError sealed classes instead of hardcoded strings, enabling full control over error message localization via stringResource() or any i18n system
formidable-persistence SavedStateHandle + retain {} strategies for surviving process death
formidable-wizard Multi-step forms: @Step, WizardState, FormWizard {}
formidable-autofill Compose 1.8 autofill semantics injection via @AutofillType
formidable-showcase Live WASM playground on GitHub Pages
Password style FieldStyle.Text.Password that bundles PasswordVisualTransformation and an eye-icon toggle
Date / time picker DateField and TimeField types with platform-native pickers
Character counter Auto-render shows remaining characters when @MaxLength is set
Theming Global default styles for auto-render fields (override OutlinedTextField defaults once, apply everywhere)
Compose Desktop target Extend CMP support beyond Android + iOS + WASM
Form serialization controller.toJson() / controller.fromJson() for draft saving
Server error DSL controller.setErrors(mapOf("username" to "Already taken")) batch API

Contributing

Contributions are welcome! Please read the contributing guidelines before submitting a pull request.

License

MIT — Copyright © 2024-present Wassim Beltaief

About

Headless, schema-driven form engine for Kotlin Multiplatform, type-safe generated controllers, built-in validation, zero reflection.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

31 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages