Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetpack Compose, custom cursor position in TextField

Tags:

How can I set the cursor in a random position on a TextField when it it get focus? The equivalent of editText.setSelection(position) with the classic android view system.

This is the code I am using to have an edit text automatically receive the focus when it is added to the screen. I would like to be able to move the cursor from the default position which is 0

val (getText, setText) = remember { mutableStateOf("hello") }
AutofocusEditText(
    text = getText,
    setText = setText
)
    
...

@Composable
private fun AutofocusEditText(
    text: String,
    setText : (String) -> Unit
) {
    val focusState = remember { mutableStateOf(FocusState.Inactive) }
    val focusRequester = FocusRequester()
    val focusModifier = Modifier.focus()
    Row(
        modifier = Modifier.focusObserver { newFocusValue -> focusState.value = newFocusValue }
    ) {
        val focusRequesterModifier =
            Modifier.focusRequester(focusRequester)

        TextField(
            value = text,
            modifier = focusModifier.then(focusRequesterModifier),
            backgroundColor = Color.Transparent,
            onValueChange = setText,
            keyboardOptions = KeyboardOptions.Default.copy(
                imeAction = ImeAction.Done
            ),
            onImeActionPerformed = { action, softKeyboardController ->
                if (action == ImeAction.Done) {
                    softKeyboardController?.hideSoftwareKeyboard()
                }
            }
        )
    }
    onActive {
        focusRequester.requestFocus()
    }
}
like image 226
Nicola De Fiorenze Avatar asked Jan 04 '21 14:01

Nicola De Fiorenze


1 Answers

You have to use the TextFieldValue version of TextField.

@Composable
fun TextField(
    value: TextFieldValue,
    onValueChange: (TextFieldValue) -> Unit,
    /* ... */) {/* Impl */}

Code examples: EDIT: Compose Version 1.1.1 (13.04.22)

enum class CursorSelectionBehaviour {
    START, END, SELECT_ALL
}

@Composable
fun AutofocusTextFieldExample(
    initValue: String,
    behaviour: CursorSelectionBehaviour = CursorSelectionBehaviour.END
) {
    val direction = LocalLayoutDirection.current
    var tfv by remember {
        val selection = when (behaviour) {
            CursorSelectionBehaviour.START -> {
                if (direction == Ltr) TextRange.Zero else TextRange(initValue.length)
            }
            CursorSelectionBehaviour.END -> {
                if (direction == Ltr) TextRange(initValue.length) else TextRange.Zero
            }
            CursorSelectionBehaviour.SELECT_ALL -> TextRange(0, initValue.length)
        }
        val textFieldValue = TextFieldValue(text = initValue, selection = selection)
        mutableStateOf(textFieldValue)
    }
    val focusRequester = remember { FocusRequester.Default }
    TextField(
        modifier = Modifier.focusRequester(focusRequester),
        value = tfv,
        onValueChange = { tfv = it }
    )
    LaunchedEffect(Unit) {
        focusRequester.requestFocus()
    }
}

OLD (04.01.21):

    @Composable
    fun AutoFocusingText() {
        val textState = remember { mutableStateOf(TextFieldValue()) }
        val focusState = remember { mutableStateOf(FocusState.Inactive) }
        val focusRequester = FocusRequester()
        val focusModifier = Modifier.focus()
        Row(
            modifier = Modifier.focusObserver { focusState.value = it }
        ) {
            val focusRequesterModifier = Modifier.focusRequester(focusRequester)
            TextField(
                modifier = focusModifier.then(focusRequesterModifier),
                value = textState.value,
                onValueChange = { value: TextFieldValue ->
                    textState.value = value
                }
            )
        }
        onActive {
            focusRequester.requestFocus()
        }
    }

If you have a non-empty string as an initial value you have to change the selection manually. Replace the empty TextFieldValue with: TextFieldValue(text = value, selection = TextRange(value.length, value.length))

When you want to extract the value like it is in your code. You either add the current selection as a parameter or extract it combined with the TextFieldValue. Otherwise, if the user edits in the middle of the text the cursor jumps back to the end on the next onValueChanged.

like image 75
2jan222 Avatar answered Sep 16 '22 16:09

2jan222