TextFieldBuffer.ChangeList


The ordered list of non-overlapping and discontinuous changes performed on a TextFieldBuffer during the current edit or filter operation. Changes are listed in the order they appear in the text, not the order in which they were made. Overlapping changes are represented as a single change.

Summary

Public functions

TextRange
getOriginalRange(changeIndex: Int)

Returns the range in the original text that was replaced.

Cmn
TextRange
getRange(changeIndex: Int)

Returns the range in the TextFieldBuffer that was changed.

Cmn

Public properties

Int

The number of changes that have been performed.

Cmn

Extension functions

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

Iterates over all changes in this ChangeList in order of their appearance in the text (from the lowest character offset to the highest).

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

Iterates over all changes in this ChangeList in reverse order of their appearance in the text (from the highest character offset down to the lowest).

Cmn

Public functions

getOriginalRange

fun getOriginalRange(changeIndex: Int): TextRange

Returns the range in the original text that was replaced.

getRange

fun getRange(changeIndex: Int): TextRange

Returns the range in the TextFieldBuffer that was changed.

Public properties

changeCount

val changeCountInt

The number of changes that have been performed.

Extension functions

TextFieldBuffer.ChangeList.forEachChange

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

Iterates over all changes in this ChangeList in order of their appearance in the text (from the lowest character offset to the highest).

In each iteration, block receives range (the range of the change in the updated TextFieldBuffer) and originalRange (the corresponding range in the original text buffer before any changes).

Avoid modifying text before the current range. Changes are ordered by character offset, so modifying text earlier in the buffer shifts the current change to a higher index in the ChangeList. Because this function iterates forward by index, it will mistakenly visit the current change again and skip the newly inserted change.

For example, assume ChangeList initially has one change at 5..8. If block inserts text at index 0, a new change at 0..2 is placed at index 0, and the original change is shifted to index 1. As the loop advances to index 1, it visits the original change a second time.

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.material3.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"""")
        }
    },
)
Parameters
block: (range: TextRange, originalRange: TextRange) -> Unit

The block to be invoked for each change.

TextFieldBuffer.ChangeList.forEachChangeReversed

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

Iterates over all changes in this ChangeList in reverse order of their appearance in the text (from the highest character offset down to the lowest).

In each iteration, block receives range (the range of the change in the updated TextFieldBuffer) and originalRange (the corresponding range in the original text buffer before any changes).

Unlike forEachChange, you may safely make non-overlapping changes after the current range without triggering repeated iterations.

Because iteration proceeds backward by index, any new changes made after the current range are assigned greater indices in the ChangeList and fall beyond the current loop index, so they are cleanly skipped.

For example, suppose ChangeList has changes at index 0 (0..2) and index 1 (10..12). This function visits index 1 first. If you then insert text at index 15, the new change is appended at index 2. Since the loop next decrements to index 0, the new change at index 2 is skipped.

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.material3.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),
                )
            }
        }
    },
)
Parameters
block: (range: TextRange, originalRange: TextRange) -> Unit

The block to be invoked for each change.

See also
forEachChange