EditText in Jetpack Compose: A Complete Guide
Jetpack Compose introduces a new way to handle user input with a composable called TextField
, which replaces the traditional EditText
used in XML-based layouts. TextField
is used to capture user input and allows for easy customization and styling. This guide will cover the basics of TextField
, how to manage input state, and various customization options in Jetpack Compose.
What is TextField in Jetpack Compose?
In Jetpack Compose, TextField
serves as the primary component for accepting text input from users, similar to EditText
in the traditional Android XML layout system. It is designed to be simple yet flexible, allowing developers to customize its look and behavior extensively.
@Composable
fun SimpleTextField() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { newText ->
text = newText
},
label = { Text("Enter your text") }
)
}
Basic Properties of TextField
The TextField
composable comes with several essential properties that allow developers to control its behavior and appearance:
- value: The current text to display in the
TextField
. This is a state variable, typically aString
. - onValueChange: A lambda function that’s triggered every time the text changes.
- label: An optional composable to display a label inside the
TextField
. - placeholder: Shows placeholder text when the field is empty.
- leadingIcon and trailingIcon: Icons placed on the left or right side of the
TextField
.
Handling State in TextField
In Jetpack Compose, handling state is critical. TextField
requires a MutableState
object to keep track of the input value. This is usually managed using the remember
and mutableStateOf
functions.
@Composable
fun StatefulTextField() {
var inputText by remember { mutableStateOf("") }
TextField(
value = inputText,
onValueChange = { newText ->
inputText = newText
},
label = { Text("Name") }
)
}
Customizing TextField Appearance
The TextField
composable allows for extensive customization of its appearance. Here are some common styling options:
- Text Color: Customizes the color of the text within the
TextField
. - Text Style: Allows setting font size, weight, and family.
- Background Color: Adds a background to the
TextField
. - Shape: Modifies the shape of the
TextField
, such as rounded corners.
@Composable
fun StyledTextField() {
var inputText by remember { mutableStateOf("") }
TextField(
value = inputText,
onValueChange = { inputText = it },
label = { Text("Email Address") },
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.LightGray,
focusedIndicatorColor = Color.Blue,
unfocusedIndicatorColor = Color.Gray,
textColor = Color.Black
),
shape = RoundedCornerShape(8.dp),
textStyle = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)
)
}
Adding Icons to TextField
You can easily add icons within a TextField
using leadingIcon
and trailingIcon
properties. This is useful for fields that require indicators or action buttons (e.g., a search or clear button).
@Composable
fun TextFieldWithIcon() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Search") },
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
trailingIcon = {
if (text.isNotEmpty()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Default.Clear, contentDescription = "Clear text")
}
}
}
)
}
Password Field with Obscured Text
To create a password input field, set visualTransformation
to PasswordVisualTransformation()
. This obscures the text to ensure privacy.
@Composable
fun PasswordTextField() {
var password by remember { mutableStateOf("") }
var isPasswordVisible by remember { mutableStateOf(false) }
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) {
Icon(
imageVector = if (isPasswordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff,
contentDescription = if (isPasswordVisible) "Hide password" else "Show password"
)
}
}
)
}
Outlined TextField
Compose provides OutlinedTextField
, a variant with an outlined style similar to the Material Design guidelines. This type of text field is helpful when you want a more emphasized border around the input.
@Composable
fun OutlinedTextFieldExample() {
var text by remember { mutableStateOf("") }
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("Outlined Text Field") },
leadingIcon = { Icon(Icons.Default.Email, contentDescription = null) }
)
}
Controlling Text Input Types
You can specify the type of input for a TextField
, such as numeric, email, or password, using the keyboardOptions
property. The keyboardType
within KeyboardOptions
adjusts the keyboard accordingly.
@Composable
fun NumberInputTextField() {
var number by remember { mutableStateOf("") }
TextField(
value = number,
onValueChange = { number = it },
label = { Text("Enter Number") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
}
Error Handling in TextField
To display errors in a TextField
, use conditional logic to show an error message based on validation criteria.
@Composable
fun TextFieldWithError() {
var text by remember { mutableStateOf("") }
var isError by remember { mutableStateOf(false) }
Column {
TextField(
value = text,
onValueChange = {
text = it
isError = text.length < 5
},
label = { Text("Username") },
isError = isError
)
if (isError) {
Text(
text = "Username must be at least 5 characters",
color = Color.Red,
style = TextStyle(fontSize = 12.sp)
)
}
}
}
Single-line vs Multi-line TextField
In Jetpack Compose, you can control the number of lines with singleLine
or by setting maxLines
. For multi-line input, specify the maxLines
property.
@Composable
fun MultilineTextField() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Multi-line Input") },
maxLines = 5
)
}
Keyboard Actions in TextField
TextField
supports keyboard actions, allowing users to control what happens when they press “Done,” “Next,” or other action buttons. You can handle this with keyboardActions
.
@Composable
fun KeyboardActionsTextField() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Email") },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = { /* Handle action */ }
)
)
}
Conclusion
The TextField
composable in Jetpack Compose provides powerful, flexible options for creating user input fields without the complexity of XML layouts. From basic input to password fields, error handling, and custom styling, TextField
covers all the typical use cases for text input in modern Android apps.
By leveraging the declarative approach of Jetpack Compose, developers can create rich and interactive text input fields that are both easy to implement and maintain. Mastering TextField
is an essential skill for any developer working with Jetpack Compose.