0Pricing
Android Academy · Lesson

TextField and User Input

Capture text from the user in Compose.

TextField and User Input is a free Android Academy lesson on CoddyKit — lesson 1 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.

Capturing User Input

Almost every real app needs to ask the user for something: a name, an email, a search query. In Jetpack Compose, the building block for text entry is TextField.

In this lesson you'll learn how to display a text field, read what the user types, and keep the field in sync with your app's state. This is the foundation for every form you'll ever build.

Your First TextField

A TextField needs two things: a value (the current text to display) and an onValueChange callback (what to do when the user types).

Compose is declarative, so the field does not store its own text. You hold the text in state and feed it back in. This pattern is called state hoisting.

@Composable
fun NameField() {
    var name by remember { mutableStateOf("") }

    TextField(
        value = name,
        onValueChange = { name = it }
    )
}

How the Value Loop Works

The single most important idea: a TextField shows exactly what you pass to value. When the user presses a key, Compose calls onValueChange with the new text.

  • You save that new text into state.
  • State changes trigger recomposition.
  • The field is re-drawn with the updated value.

If you forget to update the state inside onValueChange, the field will appear frozen because value never changes.

var name by remember { mutableStateOf("") }

TextField(
    value = name,
    onValueChange = { newText -> name = newText }
)

Adding a Label and Placeholder

A bare text field gives the user no clue what to type. Add a label (floats above the field) and a placeholder (hint shown when empty).

These small touches make forms far easier to understand.

var email by remember { mutableStateOf("") }

TextField(
    value = email,
    onValueChange = { email = it },
    label = { Text("Email") },
    placeholder = { Text("you@example.com") }
)

OutlinedTextField

Material Design offers two main styles. TextField has a filled background, while OutlinedTextField draws a border around the field. They share the same API, so switching is trivial.

Outlined fields are popular in forms because they read cleanly when stacked vertically.

var username by remember { mutableStateOf("") }

OutlinedTextField(
    value = username,
    onValueChange = { username = it },
    label = { Text("Username") },
    singleLine = true
)

Leading and Trailing Icons

You can decorate a field with icons. A leadingIcon appears at the start (great for a search or email glyph) and a trailingIcon at the end (often a clear button).

Icons make a field's purpose obvious at a glance.

var query by remember { mutableStateOf("") }

OutlinedTextField(
    value = query,
    onValueChange = { query = it },
    label = { Text("Search") },
    leadingIcon = {
        Icon(Icons.Default.Search, contentDescription = null)
    }
)

A Clear Button

A common UX pattern is a small "x" that clears the field. Show it only when there is text, using the trailingIcon slot and an IconButton.

Notice how clearing is just setting the state back to an empty string.

var text by remember { mutableStateOf("") }

OutlinedTextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Note") },
    trailingIcon = {
        if (text.isNotEmpty()) {
            IconButton(onClick = { text = "" }) {
                Icon(Icons.Default.Clear, contentDescription = "Clear")
            }
        }
    }
)

Single Line vs Multi Line

By default a TextField can grow to multiple lines. For things like a name or email, set singleLine = true so the user cannot insert a newline.

For longer notes, leave it multi-line and optionally cap the height with maxLines.

var bio by remember { mutableStateOf("") }

OutlinedTextField(
    value = bio,
    onValueChange = { bio = it },
    label = { Text("Bio") },
    maxLines = 4
)

Reacting to Input Live

Because you own the text state, you can react to it instantly. A classic example: show a live character count under the field.

Every keystroke updates text, which recomposes both the field and the count below it.

var text by remember { mutableStateOf("") }

Column {
    OutlinedTextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Tweet") }
    )
    Text("${text.length} / 280")
}

Transforming Input

Sometimes you want to normalize what the user types before storing it. Inside onValueChange you can transform the new value first.

Here we force a username to lowercase and strip spaces. Because you control the loop, the field always shows the cleaned version.

var handle by remember { mutableStateOf("") }

OutlinedTextField(
    value = handle,
    onValueChange = { input ->
        handle = input.lowercase().filterNot { it.isWhitespace() }
    },
    label = { Text("Handle") },
    singleLine = true
)

Pure Kotlin: Cleaning a Handle

The transformation logic above is just plain Kotlin. You can test it in isolation, no Android required. Here is the same cleaning rule running standalone.

Keeping logic like this in pure functions makes it easy to unit test.

fun cleanHandle(input: String): String =
    input.lowercase().filterNot { it.isWhitespace() }

fun main() {
    println(cleanHandle("  John Doe "))
    println(cleanHandle("ADA Lovelace"))
}

Quick Check

What happens if a TextField's onValueChange callback never updates the state passed to value?

Recap

You now know the core of text input in Compose:

  • TextField and OutlinedTextField take a value and an onValueChange callback.
  • You hold the text in remember { mutableStateOf(...) } and update it on every change (state hoisting).
  • label, placeholder, leading/trailing icons and singleLine shape the look and behavior.
  • Because you own the state, you can react live or transform input as the user types.

Next up: managing the state of a whole form, not just one field.

Frequently asked questions

Is the “TextField and User Input” lesson free?

Yes — the full text of “TextField and User Input” 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 “TextField and User Input”?

Capture text from the user in Compose. 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 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “TextField and User Input” 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