build

fun <A> build(contents: @Composable FormScope<A>.() -> Unit): Form<A>(source)

Basic builder for a Form.

To build a basic non-composite Form (e.x. something like a text entry field), you can simply use the FormScope.currentValue property in scope of the builder and bind that to your UI:

class StringEntry(val fieldLabel: String): Form<String> by (Form.build {
var text by currentValue

Column {
Text(fieldLabel)
BasicTextField(
value = fieldLabel,
onValueChanged = { newText ->
text = newText
}
)
}
})

However, one of the main advantages of Forms over raw composable functions is the ability to embed sub-forms when building the entry form for a larger type without much ceremony.

Like most things in Jetpack Compose, Form works with immutable data types. We use the arrow-optics ksp plugin and the @Optics annotation to automatically generate Lenses that let us easily reference a "part" of our form. For example, given the following:

@Optics
data class User(
val firstName: String,
val lastName: String,
val birthday: LocalDate
) {
companion object
}

we can do the following to build a user entry form:

class UserEntryForm(): Form<User> by (Form.build {
StringEntry("First name:")
.bind(User.firstName)

StringEntry("Last name:")
.bind(User.lastName)

LocalDateEntry()
.bind(User.birthday)
})

which can then be used like any other composable in the rest of your application with .contents().