Handle user input

TextField allows users to enter and modify text. This page describes how you can implement TextField, style TextField input, and configure other TextField options, like keyboard options and visually transforming user input.

Choose TextField implementation

There are two levels of TextField implementation:

  1. TextField is the Material Design implementation. We recommend you choose this implementation as it follows Material Design guidelines:
    • Default styling is filled
    • OutlinedTextField is the outline styling version
  2. BasicTextField enables users to edit text via hardware or software keyboard, but provides no decorations like hint or placeholder.

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

    TextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

An editable text field containing the word

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

    OutlinedTextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

An editable text field, with a purple border and label.

Style TextField

TextField and BasicTextField share a lot of common parameters to customize them. The complete list for TextField is available in the TextField source code. This is a non-exhaustive list of some of the useful parameters:

  • singleLine
  • maxLines
  • textStyle

@Composable
fun StyledTextField() {
    var value by remember { mutableStateOf("Hello\nWorld\nInvisible") }

    TextField(
        value = value,
        onValueChange = { value = it },
        label = { Text("Enter text") },
        maxLines = 2,
        textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
        modifier = Modifier.padding(20.dp)
    )
}

A multiline TextField, with two editable lines plus the label

We recommend TextField over BasicTextField when your design calls for a Material TextField or OutlinedTextField. However, BasicTextField should be used when building designs that don't need the decorations from the Material spec.

Style input with Brush API

You can use the Brush API for more advanced styling in your TextField. The following section describes how you can use a Brush to add a colored gradient to TextField input.

For more information about using the Brush API to style text, see Enable advanced styling with Brush API.

Implement colored gradients using TextStyle

To implement a colored gradient as you type within a TextField, set your brush of choice as a TextStyle for your TextField. In this example, we use a built-in brush with a linearGradient to view the rainbow gradient effect as text is typed into the TextField.

var text by remember { mutableStateOf("") }
val brush = remember {
    Brush.linearGradient(
        colors = rainbowColors
    )
}
TextField(
    value = text, onValueChange = { text = it }, textStyle = TextStyle(brush = brush)
)

Using buildAnnotatedString and SpanStyle, along with linearGradient, to customize only a piece of text.
Figure 3. Using buildAnnotatedString and SpanStyle, along with linearGradient, to customize only a piece of text.

Set keyboard options

TextField lets you set keyboard configurations options, such as the keyboard layout, or enable the autocorrect if it’s supported by the keyboard. Some options may not be guaranteed if the software keyboard doesn't comply with the options provided here. Here is the list of the supported keyboard options:

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

Format input

TextField allows you to set a VisualTransformation on the input value, like replacing characters with * for passwords, or inserting hyphens every 4 digits for a credit card number:

@Composable
fun PasswordTextField() {
    var password by rememberSaveable { mutableStateOf("") }

    TextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("Enter password") },
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
    )
}

A password text entry field, with the text masked

More examples are available in the VisualTransformationSamples source code.

Clean input

A common task when editing text is to strip leading characters, or otherwise transform the input string each time it changes.

As a model, you should assume that the keyboard may make arbitrary and large edits each onValueChange. This may happen, for example, if the user uses autocorrect, replaces a word with an emoji, or other smart editing features. To correctly handle this, write any transformation logic with the assumption that the current text passed to onValueChange is unrelated to the previous or next values that will be passed to onValueChange.

To implement a text field that disallows leading zeros, you can do this by stripping all leading zeroes on every value change.

@Composable
fun NoLeadingZeroes() {
    var input by rememberSaveable { mutableStateOf("") }
    TextField(
        value = input,
        onValueChange = { newText ->
            input = newText.trimStart { it == '0' }
        }
    )
}

To control the cursor position while cleaning text, use the TextFieldValue overload of TextField as part of the state.

Best practices with state

The following is a series of best practices to define and update TextField state to prevent input issues in your app.

  • Use MutableState to represent TextField state: Avoid using reactive streams like StateFlow to represent TextField state, as these structures might introduce asynchronous delays.

class SignUpViewModel : ViewModel() {

    var username by mutableStateOf("")
        private set

    /* ... */
}

  • Avoid delays to update the state: When you call onValueChange, update your TextField synchronously and immediately:

// SignUpViewModel.kt

class SignUpViewModel(private val userRepository: UserRepository) : ViewModel() {

    var username by mutableStateOf("")
        private set

    fun updateUsername(input: String) {
        username = input
    }
}

// SignUpScreen.kt

@Composable
fun SignUpScreen(/*...*/) {

    OutlinedTextField(
        value = viewModel.username,
        onValueChange = { username -> viewModel.updateUsername(username) }
        /*...*/
    )
}

  • Where to define the state: If your TextField state requires business logic validations as you type, it is correct to hoist the state to your ViewModel. If it doesn’t, you can use composables or a state holder class as the source of truth. To learn more about where to hoist your state, see the state hoisting documentation.