Skip to content

Jetpack Compose TextField — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about Jetpack Compose TextField. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Your TextField doesn't validate input, the keyboard doesn't dismiss on action, or the visual transformation for passwords shows characters briefly.

Wrong Approach ❌

@Composable
fun BadTextField() {
    var text by remember { mutableStateOf("") }

    OutlinedTextField(
        value = text,
        onValueChange = { text = it }, // No input filtering
        label = { Text("Age") },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
    )
    // User can type letters into a number field!
}

Output: Invalid input accepted. Keyboard doesn't respond to IME actions.

Right Approach ✅

@Composable
fun GoodTextField() {
    var text by remember { mutableStateOf("") }
    var hasError by remember { mutableStateOf(false) }

    OutlinedTextField(
        value = text,
        onValueChange = { input ->
            // Filter input — only allow digits
            val filtered = input.filter { it.isDigit() }
            if (filtered != input) hasError = true
            text = filtered
        },
        label = { Text("Age") },
        isError = hasError,
        supportingText = {
            if (hasError) Text("Only numbers are allowed", color = MaterialTheme.colorScheme.error)
        },
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Number,
            imeAction = ImeAction.Done
        ),
        keyboardActions = KeyboardActions(
            onDone = { /* dismiss keyboard, submit form */ }
        ),
        visualTransformation = VisualTransformation.None,
        singleLine = true
    )
}

// Password field
@Composable
fun PasswordField() {
    var password by remember { mutableStateOf("") }
    var visible by remember { mutableStateOf(false) }

    OutlinedTextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("Password") },
        visualTransformation = if (visible) VisualTransformation.None else PasswordVisualTransformation(),
        trailingIcon = {
            IconButton(onClick = { visible = !visible }) {
                Icon(if (visible) Icons.Default.VisibilityOff else Icons.Default.Visibility, "")
            }
        }
    )
}

Output: Proper input validation, keyboard handling, and password masking.

Prevention

  • Always filter input in onValueChange for constrained fields.
  • Configure KeyboardOptions with correct keyboardType and imeAction.
  • Use isError + supportingText for inline validation.
  • Use PasswordVisualTransformation for secure password entry.

Common Mistakes with compose text field

  1. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  2. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  3. Using return to exit a function early instead of wrapping a pure value in the monad

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### How do I show a character counter on TextField?

Add TextFieldDefaults.counterText(text.length, maxLength) to the characterCounter slot, or use the singleLine + maxLines combo for multi-line counters.

### What is the difference between TextField and OutlinedTextField?

OutlinedTextField has an outline border and a floating label. TextField uses an underline style (Material 2 style). Both are Material 3 compliant.

### How do I handle IME actions (e.g., Next, Done)?

Set KeyboardOptions(imeAction = ImeAction.Next) and KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Down) }). Use FocusManager to move focus between fields.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro