
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’ll 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:
- TextField
- OutlinedTextField
- BasicTextField
1. TextField:
It is a filled text field. It follows the material design.

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

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

In this article, we will play with the TextField API.
First, create an empty Jetpack Compose project and open MainActivity. Add a composable called MyUI() and call it from the onCreate() method.
// we need the following packages
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.outlined.Lock
import androidx.compose.material.icons.outlined.Visibility
import androidx.compose.material.icons.outlined.VisibilityOff
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme(darkTheme = false) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
MyUI()
}
}
}
}
}
}
@Composable
private fun MyUI() {
}
We will write our code in the MyUI().
The TextField API 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.
Simple TextField Example:
Let’s create a simple text field.
@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:

Let’s understand the code.
The variable value remembers the text we enter in 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. This 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("")
}
TextField(
value = value,
onValueChange = { newText ->
value = newText
},
label = { Text(text = "Name") },
placeholder = { Text(text = "Type your name") }
)
}
Output:

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("")
}
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:

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.
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
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:

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
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:

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
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:

Toggle Password using TextField:
Let’s create the following password field:

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)
}
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 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 {
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:

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.

@Composable
private fun MyUI() {
var value by remember {
mutableStateOf("")
}
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:

To create a gradient background, use Modifier‘s background() method.
@Composable
private fun MyUI() {
var value by remember {
mutableStateOf("")
}
TextField(
modifier = Modifier.background(
brush = Brush.horizontalGradient(
listOf(
Color.Green,
Color.Yellow
)
)
),
value = value,
onValueChange = { newText ->
value = newText
}
)
}
Output:

Related: How to Create Gradient Colors in Jetpack Compose?
TextField Max Length:
We can limit the number of characters by using a simple if condition in the onValueChange block.
@Composable
private fun MyUI() {
var value by remember {
mutableStateOf("")
}
val maxLength = 5
TextField(
value = value,
onValueChange = { newText ->
if (newText.length <= maxLength) value = newText
}
)
}
Output:

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("")
}
OutlinedTextField(
value = value,
onValueChange = { newText ->
value = newText
},
label = { Text(text = "Name") },
placeholder = { Text(text = "Enter your name") }
)
}
Output:

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("")
}
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:

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:
- Jetpack Compose Rounded Corner Shapes
- Compose Toggle Button with Source Code
- How to Convert SVG Icons to XML in Android Studio?
- How to Request Permissions in Jetpack Compose?
References: