androidx.compose.foundation.text.input

Interfaces

InputTransformation

A function that is ran after every change made to a TextFieldState by user input and can change or reject that input.

Cmn
KeyboardActionHandler
Cmn
OutputTransformation

A function (transformOutput) that transforms the text presented to a user by a BasicTextField.

Cmn
TextFieldBuffer.ChangeList

The ordered list of non-overlapping and discontinuous changes performed on a TextFieldBuffer during the current edit or filter operation.

Cmn
TextFieldDecorator

Composable interface that allows to add decorations around text field, such as icon, placeholder, helper messages or similar, and automatically increase the hit target area of the text field.

Cmn
TextFieldLineLimits

Values that specify the text wrapping, scrolling, and height measurement behavior for text fields.

Cmn

Classes

TextFieldBuffer

A text buffer that can be edited, similar to StringBuilder.

Cmn
TextFieldLineLimits.MultiLine

The text field will be at least minHeightInLines tall, if the text overflows it will wrap, and if the text ends up being more than one line the field will grow until it is maxHeightInLines tall and then start scrolling vertically.

Cmn
TextFieldState

The editable text state of a text field, including both the text itself and position of the cursor or selection.

Cmn
TextObfuscationMode

Defines how the text will be obscured in secure text fields.

Cmn
UndoState

Defines an interactable undo history.

Cmn

Objects

InputTransformation.Companion
Cmn
TextFieldLineLimits.SingleLine

The text field is always a single line tall, ignores newlines in the text, and scrolls horizontally when the text overflows.

Cmn
TextFieldState.Saver

Saves and restores a TextFieldState for rememberSaveable.

Cmn

Top-level functions summary

TextFieldState
@Composable
rememberTextFieldState(initialText: String, initialSelection: TextRange)

Create and remember a TextFieldState.

Cmn

Extension functions summary

InputTransformation

Returns a InputTransformation that forces all text to be uppercase.

Cmn
InputTransformation
InputTransformation.byValue(
    transformation: (current: CharSequence, proposed: CharSequence) -> CharSequence
)

Creates an InputTransformation from a function that accepts both the current and proposed TextFieldCharSequence and returns the TextFieldCharSequence to use for the field.

Cmn
Unit

Deletes all the text in the state.

Cmn
Unit
TextFieldBuffer.delete(start: Int, end: Int)

Delete the text between start (inclusive) and end (exclusive).

Cmn
inline Unit

Iterates over all the changes in this ChangeList.

Cmn
inline Unit

Iterates over all the changes in this ChangeList in reverse order.

Cmn
Unit
TextFieldBuffer.insert(index: Int, text: String)

Insert text at the given index in this value.

Cmn
InputTransformation

Returns InputTransformation that rejects input which causes the total length of the text field to be more than maxLength characters.

Cmn
Unit

Places the cursor at the end of the text.

Cmn
Unit

Places the selection around all the text.

Cmn
Unit

Sets the text in this TextFieldState to text, replacing any text that was previously there, and places the cursor at the end of the new text.

Cmn
Unit

Sets the text in this TextFieldState to text, replacing any text that was previously there, and selects all the text.

Cmn
InputTransformation

Creates a filter chain that will run next after this.

Cmn
TextFieldBuffer

Creates a temporary, mutable TextFieldBuffer representing the current state of this TextFieldState.

Cmn

Top-level functions

rememberTextFieldState

@Composable
fun rememberTextFieldState(
    initialText: String = "",
    initialSelection: TextRange = TextRange(initialText.length)
): TextFieldState

Create and remember a TextFieldState. The state is remembered using rememberSaveable and so will be saved and restored with the composition.

If you need to store a TextFieldState in another object, use the TextFieldState.Saver object to manually save and restore the state.

Parameters
initialText: String = ""

The initial text state. If a different value is passed in a subsequent recomposition, the value of the state will not be updated. To update the state after it's initialized, call methods on TextFieldState.

initialSelection: TextRange = TextRange(initialText.length)

The initial selection state. If a different value is passed in a subsequent recomposition, the value of the state will not be updated. To update the state after it's initialized, call methods on TextFieldState.

Extension functions

fun InputTransformation.allCaps(locale: Locale): InputTransformation

Returns a InputTransformation that forces all text to be uppercase.

This transformation automatically configures the keyboard to capitalize all characters.

Parameters
locale: Locale

The Locale in which to perform the case conversion.

fun InputTransformation.byValue(
    transformation: (current: CharSequence, proposed: CharSequence) -> CharSequence
): InputTransformation

Creates an InputTransformation from a function that accepts both the current and proposed TextFieldCharSequence and returns the TextFieldCharSequence to use for the field.

transformation can return either current, proposed, or a completely different value.

The selection or cursor will be updated automatically. For more control of selection implement InputTransformation directly.

import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.input.InputTransformation
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.byValue
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.unit.sp

val state = remember { TextFieldState() }
BasicTextField(
    state,
    // Reject whitespace.
    inputTransformation =
        InputTransformation.byValue { current, proposed ->
            if ("""\s""".toRegex() in proposed) current else proposed
        }
)
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.input.InputTransformation
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.byValue
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.unit.sp

val state = remember { TextFieldState() }
BasicTextField(
    state,
    // Convert tabs to spaces.
    inputTransformation =
        InputTransformation.byValue { _, proposed ->
            proposed.replace("""\t""".toRegex(), "  ")
        }
)

clearText

fun TextFieldState.clearText(): Unit

Deletes all the text in the state.

To perform more complicated edits on the text, call TextFieldState.edit. This function is equivalent to calling:

edit {
delete(0, length)
placeCursorAtEnd()
}
fun TextFieldBuffer.delete(start: Int, end: Int): Unit

Delete the text between start (inclusive) and end (exclusive). Pass 0 as start and TextFieldBuffer.length as end to delete everything in this buffer.

Parameters
start: Int

The character offset of the first character to delete.

end: Int

The character offset of the first character after the deleted range.

See also
replace
append
insert

forEachChange

@ExperimentalFoundationApi
inline fun TextFieldBuffer.ChangeList.forEachChange(
    block: (range: TextRange, originalRange: TextRange) -> Unit
): Unit

Iterates over all the changes in this ChangeList.

Changes are iterated by index, so any changes made by block after the current one will be visited by block. block should not make any new changes before the current one or changes will be visited more than once. If you need to make changes, consider using forEachChangeReversed.

import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.forEachChange
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.text.substring

// Print a log message every time the text is changed.
BasicTextField(
    state = rememberTextFieldState(),
    inputTransformation = {
        changes.forEachChange { sourceRange, replacedLength ->
            val newString = asCharSequence().substring(sourceRange)
            println("""$replacedLength characters were replaced with "$newString"""")
        }
    }
)

forEachChangeReversed

@ExperimentalFoundationApi
inline fun TextFieldBuffer.ChangeList.forEachChangeReversed(
    block: (range: TextRange, originalRange: TextRange) -> Unit
): Unit

Iterates over all the changes in this ChangeList in reverse order.

Changes are iterated by index, so block should not perform any new changes before the current one or changes may be skipped. block may make non-overlapping changes after the current one safely, such changes will not be visited.

import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.delete
import androidx.compose.foundation.text.input.forEachChange
import androidx.compose.foundation.text.input.forEachChangeReversed
import androidx.compose.foundation.text.input.insert
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material.Text
import androidx.compose.runtime.remember

// Make a text field behave in "insert mode" – inserted text overwrites the text ahead of it
// instead of being inserted.
BasicTextField(
    state = rememberTextFieldState(),
    inputTransformation = {
        changes.forEachChangeReversed { range, originalRange ->
            if (!range.collapsed && originalRange.collapsed) {
                // New text was inserted, delete the text ahead of it.
                delete(
                    range.end.coerceAtMost(length),
                    (range.end + range.length).coerceAtMost(length)
                )
            }
        }
    }
)
See also
forEachChange
fun TextFieldBuffer.insert(index: Int, text: String): Unit

Insert text at the given index in this value. Pass 0 to insert text at the beginning of this buffer, and pass TextFieldBuffer.length to insert text at the end of this buffer.

This is equivalent to calling replace(index, index, text).

Parameters
index: Int

The character offset at which to insert text.

text: String

The text to insert.

See also
replace
append
delete

maxLength

fun InputTransformation.maxLength(maxLength: Int): InputTransformation

Returns InputTransformation that rejects input which causes the total length of the text field to be more than maxLength characters.

placeCursorAtEnd

fun TextFieldBuffer.placeCursorAtEnd(): Unit

Places the cursor at the end of the text.

selectAll

fun TextFieldBuffer.selectAll(): Unit

Places the selection around all the text.

setTextAndPlaceCursorAtEnd

fun TextFieldState.setTextAndPlaceCursorAtEnd(text: String): Unit

Sets the text in this TextFieldState to text, replacing any text that was previously there, and places the cursor at the end of the new text.

To perform more complicated edits on the text, call TextFieldState.edit. This function is equivalent to calling:

edit {
replace(0, length, text)
placeCursorAtEnd()
}

setTextAndSelectAll

fun TextFieldState.setTextAndSelectAll(text: String): Unit

Sets the text in this TextFieldState to text, replacing any text that was previously there, and selects all the text.

To perform more complicated edits on the text, call TextFieldState.edit. This function is equivalent to calling:

edit {
replace(0, length, text)
selectAll()
}
fun InputTransformation.then(next: InputTransformation): InputTransformation

Creates a filter chain that will run next after this. Filters are applied sequentially, so any changes made by this filter will be visible to next.

The returned filter will use the KeyboardOptions from next if non-null, otherwise it will use the options from this transformation.

import androidx.compose.foundation.text.input.InputTransformation
import androidx.compose.foundation.text.input.then

val removeFirstEFilter = InputTransformation {
    val index = asCharSequence().indexOf('e')
    if (index != -1) {
        replace(index, index + 1, "")
    }
}
val printECountFilter = InputTransformation {
    println("found ${asCharSequence().count { it == 'e' }} 'e's in the string")
}

// Returns a filter that always prints 0 e's.
removeFirstEFilter.then(printECountFilter)

// Returns a filter that prints the number of e's before the first one is removed.
printECountFilter.then(removeFirstEFilter)
Parameters
next: InputTransformation

The InputTransformation that will be ran after this one.

toTextFieldBuffer

fun TextFieldState.toTextFieldBuffer(): TextFieldBuffer

Creates a temporary, mutable TextFieldBuffer representing the current state of this TextFieldState.

Use a TextFieldBuffer to:

  • Apply transformations for testing purposes

  • Preview how the TextField would render with a specific OutputTransformation

This is similar to calling TextFieldState.edit, but without committing the changes back to the TextFieldState.

Important: A TextFieldBuffer is intended for short-term use. Let the garbage collecter dispose of it when you're finished to avoid unnecessary memory usage.

import androidx.compose.foundation.text.input.OutputTransformation
import androidx.compose.foundation.text.input.TextFieldBuffer
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.insert
import androidx.compose.foundation.text.input.toTextFieldBuffer
import androidx.compose.material.Text

val state = TextFieldState("Hello, World")
val outputTransformation = OutputTransformation { insert(0, "> ") }

val buffer = state.toTextFieldBuffer()
with(outputTransformation) { buffer.transformOutput() }

val transformedText = buffer.asCharSequence()
val transformedSelection = buffer.selection