Jetpack Compose TextField (with Examples)

Jetpack Compose TextField

TextField in Jetpack Compose is a UI element that allows the user to enter the text. If you are familiar with XML, it is the EditText. In this article, we will explore TextField with examples.

Prerequisites:

What is TextField in Jetpack Compose?

TextField is used to get the data from the user. For example, a signup page that contains a name, phone number, password, etc.

Jetpack Compose provides 3 TextField APIs:

  1. TextField
  2. OutlinedTextField
  3. BasicTextField

1. TextField:

It is a filled text field. It follows the material design.

Jetpack Compose TextField Example

2. OutlinedTextField:

It is an outlined text field with less visual emphasis. It shows a beautiful border around it and follows material design too.

Outlined Text Field Example

3. BasicTextField:

It is a basic composable with no decorations. We have to define everything.

BasicTextField Example

In this article, we will play with the TextField API. It looks like this:

@Composable
fun TextField(
    value: TextFieldValue,
    onValueChange: (TextFieldValue) -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    readOnly: Boolean = false,
    textStyle: TextStyle = LocalTextStyle.current,
    label: @Composable (() -> Unit)? = null,
    placeholder: @Composable (() -> Unit)? = null,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    isError: Boolean = false,
    visualTransformation: VisualTransformation = VisualTransformation.None,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions(),
    singleLine: Boolean = false,
    maxLines: Int = Int.MAX_VALUE,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    shape: Shape = TextFieldDefaults.TextFieldShape,
    colors: TextFieldColors = TextFieldDefaults.textFieldColors()
)

Don’t get scared of the parameters. Instead of looking at each one, let’s understand them with use cases.

For this article, create a composable called MyUI() and call it inside the onCreate(). We will write our code in it.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme(darkTheme = false) {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    MyUI()
                }
            }
        }
    }
}

@Composable
private fun MyUI() {

}

Simple TextField Example:

Let’s create a simple text filed.

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

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

        Text(text = "Input Text: $value")
    }
}

Output:

TextField Example

Let’s understand the code.

The variable value remembers the state of the TextField. Its data type is String. You can use this to get real-time updates from the TextField.

Next, in the Column layout, we added TextField() and Text() composables. In the TextField, we are using two mandatory parameters – value and onValueChange.

value – This is the text to be shown in the field. We passed our value to it.

onValueChange – It is a lambda that gets called every time the user updates the data in the field. It provides the new (updated) text. We assigned it to our value variable.

Initially, the value is an empty string. That is why the text field is empty. When the user enters the data, onValueChange is called which contains the new text. We assigned it to our value variable. As a result, the value is updated and shown in the field.

TextField Placeholder and Label:

There are two parameters – label and placeholder.

label – This is an optional text. It is displayed at the top left corner when the text field is in focus and inside the container when the text field is not in focus.

placeholder – An optional text to be displayed when the text field is in focus and the input text is empty.

Example:

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        TextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            label = { Text(text = "Name") },
            placeholder = { Text(text = "Type your name") }
        )
    }
}

Output:

Placeholder and Label

TextField Icons:

We can add two icons to the TextField.

leadingIcon – Placed at the start of the text field container.

trailingIcon – Placed at the end of the text field container.

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        TextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            leadingIcon = { Icon(imageVector = Icons.Default.Email, contentDescription = "Email Icon") },
            trailingIcon = { Icon(imageVector = Icons.Default.Person, contentDescription = "Person Icon") },
            label = { Text(text = "Email") },
            placeholder = { Text(text = "Type your email") }
        )
    }
}

Output:

Leading and Trailing Icons

TextField Keyboard Options and Actions:

There are two parameters for customizing the keywboard – keyboardOptions and keyboardActions.

Keyboard Options:

It is used to change the input type. It looks like this:

class KeyboardOptions constructor(
    val capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
    val autoCorrect: Boolean = true,
    val keyboardType: KeyboardType = KeyboardType.Text,
    val imeAction: ImeAction = ImeAction.Default
)

It takes 4 parameters:

1. Capitalization:

We can request the keyboard to capitalize the text. It has multiple options.

KeyboardCapitalization.None – Do not auto-capitalize text.

KeyboardCapitalization.Characters – Capitalize all characters.

KeyboardCapitalization.Words – Capitalize the first character of every word.

KeyboardCapitalization.Sentences – Capitalize the first character of each sentence.

2. Auto Correct:

A boolean value that informs the keyboard whether to enable auto-correct.

3. Keyboard Type:

This is the type of keyboard, for example, text or number pad. Available options:

KeyboardType.Text – Regular keyboard.

KeyboardType.Ascii – Users can enter ASCII characters.

KeyboardType.Number – For digits.

KeyboardType.Phone – For mobile numbers.

KeyboardType.Uri – For URIs.

KeyboardType.Email – For email addresses.

KeyboardType.Password – For passwords.

KeyboardType.NumberPassword – For number passwords.

KeyboardType.Decimal – For decimal numbers. In this case, the keyboard explicitly provides a decimal separator as input, which is not assured by KeyboardType.Number.

4. IME Action:

It signals the keyboard what type of action should be displayed. The Enter key on the keyboard is changed according to the action.

ImeAction.Go – Represents that the user would like to go to the target of the text in the input i.e. visiting a URL.

ImeAction.Search – To execute a search.

ImeAction.Send – To send a text, for example, SMS.

ImeAction.Previous – To go back to the previous text field in a form.

ImeAction.Next – To move to the new line or next text field in a form.

ImeAction.Done – It represents the user done providing the input. For example, the last text field in a form.

ImeAction.None – It represents that no action is expected from the keyboard. The keyboard might choose to show an action which mostly will be a new line.

ImeAction.Default – This is the default. The keyboards will mostly show one of ImeAction.Done or ImeAction.None actions.

Note: It is not guaranteed that the software keyboard will comply with the options provided above. The keyboard may or may not show the requested configuration.

Keyboard Actions:

It is a lambda that gets called when the above imeAction is triggered. Available options:

  • onDone
  • onGo
  • onNext
  • onPrevious
  • onSearch
  • onSend

Example:

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    val context = LocalContext.current.applicationContext

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        TextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            label = { Text(text = "Number") },
            placeholder = { Text(text = "Enter your number") },
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Number,
                imeAction = ImeAction.Search
            ),
            keyboardActions = KeyboardActions(
                onSearch = {
                    Toast.makeText(context, "On Search Click: value = $value", Toast.LENGTH_SHORT).show()
                }
            )
        )
    }
}

Output:

Textfield Input Type

In the above output, the keyboard is open even after onSearch is triggered. We can fix it in two ways.

The first one is to use SoftwareKeyboardController API. It closes the keyboard without removing the focus from the TextField. We can get its object from LocalSoftwareKeyboardController.

val keyboardController = LocalSoftwareKeyboardController.current
keyboardController?.hide() // call this method to close the keyboard

Example:

@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    val context = LocalContext.current.applicationContext

    val keyboardController = LocalSoftwareKeyboardController.current

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        TextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            label = { Text(text = "Number") },
            placeholder = { Text(text = "Enter your number") },
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Number,
                imeAction = ImeAction.Search
            ),
            keyboardActions = KeyboardActions(
                onSearch = {
                    // close the keyboard
                    keyboardController?.hide()

                    Toast.makeText(context, "On Search Click: value = $value", Toast.LENGTH_SHORT)
                        .show()
                }
            )
        )
    }
}

Output:

Keyboard Focus

The second method is to use the FocusManager. It closes the keyboard and removes the focus from the TextField.

val focusManager = LocalFocusManager.current
focusManager.clearFocus()

Example:

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    val context = LocalContext.current.applicationContext

    val focusManager = LocalFocusManager.current

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        TextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            label = { Text(text = "Number") },
            placeholder = { Text(text = "Enter your number") },
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Number,
                imeAction = ImeAction.Search
            ),
            keyboardActions = KeyboardActions(
                onSearch = {
                    // closes the keyboard and removes the focus from the TextField
                    focusManager.clearFocus()

                    Toast.makeText(context, "On Search Click: value = $value", Toast.LENGTH_SHORT)
                        .show()
                }
            )
        )
    }
}

Output:

Keyboard Focus 2

Toggle Password using TextField:

Let’s create the following password field:

Jetpack Compose TextField Password

To mask the text, we can use the visualTransformation parameter. Jetpack Compose provides PasswordVisualTransformation out of the box.

class PasswordVisualTransformation(val mask: Char = '\u2022')

Here, mask is the character that replaces the original text.

For the icons, you need to add the material icon dependency.

Here is the complete code:

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    var showPassword by remember {
        mutableStateOf(false)
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        TextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            label = { Text(text = "Password") },
            placeholder = { Text(text = "Enter your password") },
            leadingIcon = {
                Icon(
                    imageVector = Icons.Outlined.Lock,
                    contentDescription = "Lock Icon"
                )
            },
            trailingIcon = {
                IconButton(onClick = { showPassword = !showPassword }) {
                    Icon(
                        imageVector = if (showPassword) Icons.Outlined.VisibilityOff else Icons.Outlined.Visibility,
                        contentDescription = if (showPassword) "Show Password" else "Hide Password"
                    )
                }
            },
            visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation()
        )
    }
}

Output:

TextField Password

TextField Validation and Error Message:

TextField has isError parameter. If we set it to true, the border color will be changed to red. To show the error message, we can use the Text() composable.

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    var isUserBelow18 by remember {
        mutableStateOf(false)
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        Column {

            TextField(
                value = value,
                onValueChange = { newText ->
                    value = newText
                    isUserBelow18 = false
                },
                label = { Text(text = "Age") },
                placeholder = { Text(text = "Enter your age") },
                isError = isUserBelow18,
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Number,
                    imeAction = ImeAction.Done
                ),
                keyboardActions = KeyboardActions(
                    onDone = {
                        // validate here
                        isUserBelow18 = validateAge(inputText = value)
                    }
                )
            )

            if (isUserBelow18) {
                Text(
                    modifier = Modifier.padding(start = 16.dp),
                    text = "You should be 18+",
                    color = MaterialTheme.colors.error
                )
            }
        }
    }
}

private fun validateAge(inputText: String): Boolean {
    return inputText.toInt() < 18
}

Output:

Jetpack Compose TextField Error Message

TextField Background and Cursor Colors:

TextField() provides colors parameter. We can easily customize the colors by calling TextFieldDefaults.textFieldColors(). We can change almost any color using the method.

Jetpack Compose textfiled background cursor color
@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        TextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            label = { Text(text = "Name") },
            placeholder = { Text(text = "Enter your name") },
            colors = TextFieldDefaults.textFieldColors(
                backgroundColor = Color.Green.copy(alpha = 0.4f),
                cursorColor = Color.Yellow
            )
        )
    }
}

Output:

Cursor and Background Colors

Now, let’s look at the OutlinedTextField().

OutlinedTextField:

It shows a beautiful border around the container.

Example:

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        OutlinedTextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            label = { Text(text = "Name") },
            placeholder = { Text(text = "Enter your name") }
        )
    }
}

Output:

Outlined Text Field Example

It has the same parameters as TextField(). So, you can customize it as we did with TextField(). But there are some exceptions. For example, you have to use TextFieldDefaults.outlinedTextFieldColors() to change the colors.

@Composable
private fun MyUI() {

    var value by remember {
        mutableStateOf("")
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {

        OutlinedTextField(
            value = value,
            onValueChange = { newText ->
                value = newText
            },
            label = { Text(text = "Name") },
            placeholder = { Text(text = "Enter your name") },
            colors = TextFieldDefaults.outlinedTextFieldColors(
                backgroundColor = Color.Green.copy(alpha = 0.4f),
                cursorColor = Color.Yellow
            )
        )
    }
}

Output:

Outlined Colors

This is all about the TextField in Jetpack Compose. I hope you have learned something new. If you have any doubts, leave a comment below.

Related Articles:

References:

Leave a Comment