0Pricing
Android Academy · Lesson

Managing Form State

Hold and update field values with state.

Managing Form State is a free Android Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Android Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

From One Field to a Form

A single text field is easy. A real form has several: name, email, password, maybe a checkbox. In this lesson you'll learn clean ways to hold and update the state for an entire form in Jetpack Compose.

Get this right and validation, submission and previews all become simple.

Several Independent States

The simplest approach: one mutableStateOf per field. Each field reads and updates its own piece of state.

This is perfectly fine for small forms with two or three fields.

@Composable
fun LoginForm() {
    var email by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }

    Column {
        OutlinedTextField(email, { email = it }, label = { Text("Email") })
        OutlinedTextField(password, { password = it }, label = { Text("Password") })
    }
}

Grouping State in a Data Class

As forms grow, many loose variables get messy. A cleaner option is to model the whole form as one immutable data class.

You keep a single piece of state holding that object, and update it with copy().

data class SignUpState(
    val name: String = "",
    val email: String = "",
    val agreedToTerms: Boolean = false
)

Updating with copy()

Because the state object is immutable, you never mutate it in place. Instead you create a new copy with one field changed and assign it back.

This keeps Compose's state model predictable: a new object means a new snapshot, which triggers recomposition.

var form by remember { mutableStateOf(SignUpState()) }

OutlinedTextField(
    value = form.name,
    onValueChange = { form = form.copy(name = it) },
    label = { Text("Name") }
)

A Full Data-Class Form

Here the whole form is driven by one SignUpState. Each field updates a single property via copy().

Notice how easy it is to read the entire form's value at any moment: it's just form.

var form by remember { mutableStateOf(SignUpState()) }

Column {
    OutlinedTextField(
        value = form.name,
        onValueChange = { form = form.copy(name = it) },
        label = { Text("Name") }
    )
    OutlinedTextField(
        value = form.email,
        onValueChange = { form = form.copy(email = it) },
        label = { Text("Email") }
    )
}

Checkboxes and Switches

Not all input is text. A Checkbox or Switch works the same way: a boolean value plus an onCheckedChange callback.

Store the boolean in your form state just like a string.

var form by remember { mutableStateOf(SignUpState()) }

Row(verticalAlignment = Alignment.CenterVertically) {
    Checkbox(
        checked = form.agreedToTerms,
        onCheckedChange = { form = form.copy(agreedToTerms = it) }
    )
    Text("I agree to the terms")
}

Deriving Values from State

One big benefit of single-source state: you can compute things from it without storing extra variables. For example, whether the form is ready to submit.

Use derivedStateOf (or a simple expression) so the result updates automatically as the form changes.

var form by remember { mutableStateOf(SignUpState()) }

val canSubmit by remember {
    derivedStateOf {
        form.name.isNotBlank() &&
        form.email.isNotBlank() &&
        form.agreedToTerms
    }
}

Button(onClick = { /* submit */ }, enabled = canSubmit) {
    Text("Sign Up")
}

Surviving Configuration Changes

remember keeps state across recompositions, but it is lost on a configuration change like a screen rotation. To keep simple values across rotation, use rememberSaveable.

It works for primitives and Parcelable types out of the box.

var email by rememberSaveable { mutableStateOf("") }

OutlinedTextField(
    value = email,
    onValueChange = { email = it },
    label = { Text("Email") }
)

Hoisting Form State to a ViewModel

For anything beyond a trivial form, hold the state in a ViewModel. It survives configuration changes and keeps UI logic out of the composable.

The composable reads state and forwards events; the ViewModel owns the truth.

class SignUpViewModel : ViewModel() {
    var state by mutableStateOf(SignUpState())
        private set

    fun onNameChange(value: String) {
        state = state.copy(name = value)
    }
}

Connecting the ViewModel

In the composable, grab the ViewModel and bind each field to its state and handler. The UI stays thin and testable.

This is the recommended structure for production forms.

@Composable
fun SignUpScreen(vm: SignUpViewModel = viewModel()) {
    OutlinedTextField(
        value = vm.state.name,
        onValueChange = vm::onNameChange,
        label = { Text("Name") }
    )
}

Pure Kotlin: copy() in Action

The copy() update pattern is just standard Kotlin data-class behavior. Here it is running standalone so you can see how each update produces a brand-new object.

data class SignUpState(
    val name: String = "",
    val email: String = ""
)

fun main() {
    var state = SignUpState()
    state = state.copy(name = "Ada")
    state = state.copy(email = "ada@dev.io")
    println(state)
}

Quick Check

You model your form as an immutable data class held in state. How should you update a single field?

Recap

You learned how to manage state for whole forms:

  • Small forms: one mutableStateOf per field.
  • Larger forms: a single immutable data class, updated with copy().
  • Booleans drive Checkbox and Switch the same way text drives fields.
  • Derive values like "can submit" instead of storing them.
  • Use rememberSaveable for rotation, and a ViewModel for real screens.

Next: validating that input and showing helpful errors.

Frequently asked questions

Is the “Managing Form State” lesson free?

Yes — the full text of “Managing Form State” is free to read here on the web, and the Android Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Android Academy course, upgrade to CoddyKit PRO.

What will I learn in “Managing Form State”?

Hold and update field values with state. You practise Android Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Android Academy?

No prior experience is required. Android Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Managing Form State” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Android Academy lesson?

Yes. Every Android Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. TextField and User Input
  2. Managing Form State
  3. Validating Input
  4. Keyboard Options and Actions
← Back to Android Academy