InputTransformation



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

Input transformations are ran after hardware and software keyboard events, when text is pasted or dropped into the field, or when an accessibility service changes the text.

To chain filters together, call then.

Prebuilt filters are provided for common filter operations. See:

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.delete
import androidx.compose.foundation.text.input.insert
import androidx.compose.material.Text
import androidx.compose.runtime.remember

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

    fun CharSequence.countPrefix(char: Char): Int {
        var i = 0
        while (i < length && get(i) == char) i++
        return i
    }

    // Step one: Figure out the insertion point.
    val newPromptChars = asCharSequence().countPrefix(promptChar)
    val insertionPoint = if (newPromptChars == 0) 0 else 1

    // Step two: Ensure text is placed at the insertion point.
    if (changes.changeCount == 1) {
        val insertedRange = changes.getRange(0)
        val replacedRange = changes.getOriginalRange(0)
        if (!replacedRange.collapsed && insertedRange.collapsed) {
            // Text was deleted, delete forwards from insertion point.
            delete(insertionPoint, insertionPoint + replacedRange.length)
        }
    }
    // Else text was replaced or there were multiple changes - don't handle.

    // Step three: Ensure the prompt character is there.
    if (newPromptChars == 0) {
        insert(0, ">")
    }

    // Step four: Ensure the cursor is ready for the next input.
    placeCursorAfterCharAt(0)
})

Summary

Public companion functions

open Unit

The transform operation.

Cmn

Public functions

open Unit

Optional semantics configuration that can update certain characteristics of the applied TextField, e.g. SemanticsPropertyReceiver.maxTextLength.

Cmn
Unit

The transform operation.

Cmn

Public properties

open KeyboardOptions?

Optional KeyboardOptions that will be used as the default keyboard options for configuring the IME.

Cmn

Extension functions

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 old and proposed TextFieldCharSequence and returns the TextFieldCharSequence to use for the field.

Cmn
InputTransformation

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

Cmn
InputTransformation

Creates a filter chain that will run next after this.

Cmn

Public companion functions

transformInput

open fun TextFieldBuffer.transformInput(): Unit

The transform operation. For more information see the documentation on InputTransformation.

This function is scoped to TextFieldBuffer, a buffer that can be changed in-place to alter or reject the changes or set the selection.

To reject all changes in the scoped TextFieldBuffer, call revertAllChanges.

When multiple InputTransformations are linked together, the transformInput function of the first transformation is invoked before the second one. Once the changes are made to TextFieldBuffer by the initial InputTransformation in the chain, the same instance of TextFieldBuffer is forwarded to the subsequent transformation in the chain. Note that TextFieldBuffer.originalValue never changes while the buffer is passed along the chain. This sequence persists until the chain reaches its conclusion.

Public functions

applySemantics

open fun SemanticsPropertyReceiver.applySemantics(): Unit

Optional semantics configuration that can update certain characteristics of the applied TextField, e.g. SemanticsPropertyReceiver.maxTextLength.

transformInput

fun TextFieldBuffer.transformInput(): Unit

The transform operation. For more information see the documentation on InputTransformation.

This function is scoped to TextFieldBuffer, a buffer that can be changed in-place to alter or reject the changes or set the selection.

To reject all changes in the scoped TextFieldBuffer, call revertAllChanges.

When multiple InputTransformations are linked together, the transformInput function of the first transformation is invoked before the second one. Once the changes are made to TextFieldBuffer by the initial InputTransformation in the chain, the same instance of TextFieldBuffer is forwarded to the subsequent transformation in the chain. Note that TextFieldBuffer.originalValue never changes while the buffer is passed along the chain. This sequence persists until the chain reaches its conclusion.

Public properties

keyboardOptions

open val keyboardOptionsKeyboardOptions?

Optional KeyboardOptions that will be used as the default keyboard options for configuring the IME. The options passed directly to the text field composable will always override this.

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 old and proposed TextFieldCharSequence and returns the TextFieldCharSequence to use for the field.

transformation can return either old, 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(), "  ")
    }
)
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.

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.