16

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() } } 

3 Answers 3

19

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() } 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.

Sign up to request clarification or add additional context in comments.

4 Comments

Such an enormous pain to do such a simple thing. Cursor at the end should be the default behavior, imo. At the very minimum, TextField should have an optional constructor parameter autoFocus: AutoFocus = AutoFocus.Start so that developers won't be forced to write their own solution.
@kc_dev Sorry, if this is not the case, but somebody might consider my comment useful. You can also use the textStyle constructor parameter of the TextField to change the position of the cursor. The cursor will follow the textAlign parameter of the textStyle. TextField(... textStyle = TextStyle( ... textAlign = TextAlign.Start ))
The cursor will follow to the side of the alignment, but it does not move to the correct position in the text. I updated the code, if you want to have the cursor at the end of already existing text selection must be used. Alignment will just align the text alongside the cursor not the cursor in a text.
So if I want to use one of the function overloads that don't use TextFieldValue then what? If there are no other options then the other overloads are implicitly useless. Compose has been stable for nearly 2 years, how can this be acceptable?
1

There is another way to control the cursor position without using LaunchedEffect to call focusRequester.requestFocus(). You can use call it in onGloballyPositioned. You can also show the keyboard in onGloballyPositioned if needed.

 val focusRequester = remember { FocusRequester() } val keyboard = LocalSoftwareKeyboardController.current val textFieldValue = TextFieldValue(text = initValue, selection = TextRange(initValue.length)) //place cursor at the end of the text var tfv by remember { mutableStateOf(textFieldValue) } TextField( modifier = Modifier .focusRequester(focusRequester) .onGloballyPositioned { focusRequester.requestFocus() // IMPORTANT keyboard?.show() }, value = tfv, onValueChange = { tfv = it } ) 

Comments

0

When the input is not updated just by the user and is also changed from outside of the component, this is a way to update the content while letting the user play with the cursor:

private fun InputField(input:String) { val inputLength = input.length val textFieldValue = TextFieldValue(input, TextRange(inputLength)) var textFieldValueRembered by remember { mutableStateOf(textFieldValue) } //a way to detect a reformation of text while letting the user control the cursor location if (input != textFieldValue.text) textFieldValueRembered = textFieldValue TextField( textFieldValue = textFieldValueRembered, onValueChange = { onValueChange(it.text) phoneInputField = it } ) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.