TextFieldState

Class

Common
class TextFieldState
internal constructor(
    initialText: String,
    initialSelection: TextRange,
    initialTextUndoManager: TextUndoManager,
)

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

To change the text field contents programmatically, call edit, setTextAndSelectAll, setTextAndPlaceCursorAtEnd, or clearText. Individual parts of the state like text, selection, or composition can be read from any snapshot restart scope like Composable functions. To observe these members from outside a restart scope, use snapshotFlow { textFieldState.text } or snapshotFlow { textFieldState.selection }.

When instantiating this class from a composable, use rememberTextFieldState to automatically save and restore the field state. For more advanced use cases, pass TextFieldState.Saver to rememberSaveable.

Secondary Constructors

@RememberInComposition
constructor(
    initialText: String = "",
    initialSelection: TextRange = TextRange(initialText.length),
) : this(initialText, initialSelection, TextUndoManager())

Properties

Common
val text: CharSequence

The current text content. This value will automatically update when the user enters text or otherwise changes the text field contents. To change it programmatically, call edit.

To observe changes to this property outside a restartable function, use snapshotFlow { text }.

Common
val selection: TextRange

The current selection range. If the selection is collapsed, it represents cursor location. This value will automatically update when the user enters text or otherwise changes the text field selection range. To change it programmatically, call edit.

To observe changes to this property outside a restartable function, use snapshotFlow { selection }.

Common
val composition: TextRange?

The current composing range dictated by the IME. If null, there is no composing region.

To observe changes to this property outside a restartable function, use snapshotFlow { composition }.

Common
@ExperimentalFoundationApi val undoState: UndoState

Undo history controller for this TextFieldState.

Functions

inline fun edit(block: TextFieldBuffer.() -> Unit)

Runs block with a mutable version of the current state. The block can make changes to the text and cursor/selection. See the documentation on TextFieldBuffer for a more detailed description of the available operations.

Make sure that you do not make concurrent calls to this function or call it again inside block's scope. Doing either of these actions will result in triggering an IllegalStateException.

Code Examples

BasicTextFieldStateCompleteSample

fun BasicTextFieldStateCompleteSample() {
    class SearchViewModel(val searchFieldState: TextFieldState = TextFieldState()) {
        private val queryValidationRegex = """\w+""".toRegex()
        // Use derived state to avoid recomposing every time the text changes, and only recompose
        // when the input becomes valid or invalid.
        val isQueryValid by derivedStateOf {
            // This lambda will be re-executed every time inputState.text changes.
            searchFieldState.text.matches(queryValidationRegex)
        }
        var searchResults: List<String> by mutableStateOf(emptyList())
            private set
        /** Called while the view model is active, e.g. from a LaunchedEffect. */
        suspend fun run() {
            snapshotFlow { searchFieldState.text }
                .collectLatest { queryText ->
                    // Start a new search every time the user types something valid. If the previous
                    // search is still being processed when the text is changed, it will be
                    // cancelled
                    // and this code will run again with the latest query text.
                    if (isQueryValid) {
                        searchResults = performSearch(query = queryText)
                    }
                }
        }
        fun clearQuery() {
            searchFieldState.setTextAndPlaceCursorAtEnd("")
        }
        private suspend fun performSearch(query: CharSequence): List<String> {
            TODO()
        }
    }
    @Composable
    fun SearchScreen(viewModel: SearchViewModel) {
        Column {
            Row {
                BasicTextField(viewModel.searchFieldState)
                IconButton(onClick = { viewModel.clearQuery() }) {
                    Icon(Icons.Default.Clear, contentDescription = "clear search query")
                }
            }
            if (!viewModel.isQueryValid) {
                Text("Invalid query", style = TextStyle(color = Color.Red))
            }
            LazyColumn { items(viewModel.searchResults) { TODO() } }
        }
    }
}