Composable Function

BasicTextField

Interactive text input field without decorations.

BasicTextFieldDecoratorSample

@Sampled
@Composable
fun BasicTextFieldDecoratorSample() {
    // Demonstrates how to use the decorator API on BasicTextField
    val state = rememberTextFieldState("Hello, World!")
    BasicTextField(
        state = state,
        decorator = { innerTextField ->
            // Because the decorator is used, the whole Row gets the same behaviour as the internal
            // input field would have otherwise. For example, there is no need to add a
            // `Modifier.clickable` to the Row anymore to bring the text field into focus when user
            // taps on a larger text field area which includes paddings and the icon areas.
            Row(
                Modifier.background(Color.LightGray, RoundedCornerShape(percent = 30))
                    .padding(16.dp)
            ) {
                Icon(Icons.Default.MailOutline, contentDescription = "Mail Icon")
                Spacer(Modifier.width(16.dp))
                innerTextField()
            }
        },
    )
}

BasicTextFieldCustomInputTransformationSample

@Sampled
@Composable
fun BasicTextFieldCustomInputTransformationSample() {
    // Demonstrates how to create a custom and relatively complex InputTransformation.
    val state = remember { TextFieldState() }
    BasicTextField(
        state,
        inputTransformation =
            InputTransformation {
                // A filter that always places newly-input text at the start of the string, after a
                // prompt character, like a shell.
                val promptChar = '>'

BasicTextFieldWithValueOnValueChangeSample

@Sampled
@Composable
fun BasicTextFieldWithValueOnValueChangeSample() {
    var text by remember { mutableStateOf("") }
    // A reference implementation that demonstrates how to create a TextField with the legacy
    // state hoisting design around `BasicTextField(TextFieldState)`
    StringTextField(value = text, onValueChange = { text = it })
}

BasicTextFieldWithStringSample

@Sampled
@Composable
fun BasicTextFieldWithStringSample() {
    var value by rememberSaveable { mutableStateOf("initial value") }
    BasicTextField(
        value = value,
        onValueChange = {
            // it is crucial that the update is fed back into BasicTextField in order to
            // see updates on the text
            value = it
        },
    )
}

PlaceholderBasicTextFieldSample

@Sampled
@Composable
fun PlaceholderBasicTextFieldSample() {
    var value by rememberSaveable { mutableStateOf("initial value") }
    Box {
        BasicTextField(value = value, onValueChange = { value = it })
        if (value.isEmpty()) {
            Text(text = "Placeholder")
        }
    }
}

TextFieldWithIconSample

@Sampled
@Composable
fun TextFieldWithIconSample() {
    var value by rememberSaveable { mutableStateOf("initial value") }
    BasicTextField(
        value = value,
        onValueChange = { value = it },
        decorationBox = { innerTextField ->
            // Because the decorationBox is used, the whole Row gets the same behaviour as the
            // internal input field would have otherwise. For example, there is no need to add a
            // Modifier.clickable to the Row anymore to bring the text field into focus when user
            // taps on a larger text field area which includes paddings and the icon areas.
            Row(
                Modifier.background(Color.LightGray, RoundedCornerShape(percent = 30))
                    .padding(16.dp)
            ) {
                Icon(Icons.Default.MailOutline, contentDescription = null)
                Spacer(Modifier.width(16.dp))
                innerTextField()
            }
        },
    )
}

CreditCardSample

@Sampled
@Composable
fun CreditCardSample() {
    /** The offset translator used for credit card input field */
    val creditCardOffsetTranslator =
        object : OffsetMapping {
            override fun originalToTransformed(offset: Int): Int {
                return when {
                    offset < 4 -> offset
                    offset < 8 -> offset + 1
                    offset < 12 -> offset + 2
                    offset <= 16 -> offset + 3
                    else -> 19
                }
            }

            override fun transformedToOriginal(offset: Int): Int {
                return when {
                    offset <= 4 -> offset
                    offset <= 9 -> offset - 1
                    offset <= 14 -> offset - 2
                    offset <= 19 -> offset - 3
                    else -> 16
                }
            }
        }

    /**
     * Converts up to 16 digits to hyphen connected 4 digits string. For example, "1234567890123456"
     * will be shown as "1234-5678-9012-3456"
     */
    val creditCardTransformation = VisualTransformation { text ->
        val trimmedText = if (text.text.length > 16) text.text.substring(0..15) else text.text
        var transformedText = ""
        trimmedText.forEachIndexed { index, char ->
            transformedText += char
            if ((index + 1) % 4 == 0 && index != 15) transformedText += "-"
        }
        TransformedText(AnnotatedString(transformedText), creditCardOffsetTranslator)
    }

    var text by rememberSaveable { mutableStateOf("") }
    BasicTextField(
        value = text,
        onValueChange = { input ->
            if (input.length <= 16 && input.none { !it.isDigit() }) {
                text = input
            }
        },
        modifier = Modifier.size(170.dp, 30.dp).background(Color.LightGray).wrapContentSize(),
        singleLine = true,
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
        visualTransformation = creditCardTransformation,
    )
}

BasicTextFieldSample

@Sampled
@Composable
fun BasicTextFieldSample() {
    var value by
        rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
    BasicTextField(
        value = value,
        onValueChange = {
            // it is crucial that the update is fed back into BasicTextField in order to
            // see updates on the text
            value = it
        },
    )
}
Jetpack Compose beyond MaterialFree components, screens and templates for apps that don’t look like Google’s. Copy them into your app, or let your coding agent add them.Browse for free

Content updated: