84

How to auto focus on textfield in jetpack compose. When i click on textfield and start typing on it. Meantime when i click back button,then when i'm try to click on textfield, nothing happens.

val textState = remember { mutableStateOf(TextFieldValue()) }
TextField(
    modifier = Modifier.fillMaxWidth(),
    value = textState.value,
    onValueChange = { value : TextFieldValue ->
        textState.value = value
    }
)
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
Mukul jangir
  • 941
  • 1
  • 4
  • 3

7 Answers7

107

Posting an updated Answer to the question (APIs were renamed in Compose Beta)

@Composable
fun AutoFocusingText() {
    var value by mutableStateOf("Enter Text")
    val focusRequester = remember { FocusRequester() }
    TextField(
        value = value, 
        onValueChange = { value = it },
        modifier = Modifier.focusRequester(focusRequester)
    )
    
    LaunchedEffect(Unit) {
        focusRequester.requestFocus()
    }
}
vovahost
  • 34,185
  • 17
  • 113
  • 116
Ralston
  • 1,284
  • 1
  • 8
  • 3
  • 6
    This works but sets the cursor at the beginning of the word. How would one set the cursor at the end of the word when requesting focus? – Charles Woodson May 05 '21 at 21:59
  • 3
    @CharlesWoodson you'll need to use `TextFieldValue` in the `TextField`'s value. You can set selection in `TextFieldValue` – dumbfingers Jul 06 '21 at 15:38
  • 8
    @CharlesWoodson If you are still looking to set the cursor to the end of the string in the textfield then here is how you would do it val textFieldValue = remember { mutableStateOf( TextFieldValue(someString, TextRange(someString.length)) ) Set the value of the TextField to be textFieldValue.value like this TextField( value = textFieldValue.value ) This will make the textfield to be pre populated with someString and the cursor position will be the length of the someString which is at the end of the string. – Anudeep Ananth Jul 12 '21 at 06:08
  • 1
    Wouldn't `LaunchedEffect` be better to use instead of `DisposableEffect`? – Anigif Dec 08 '21 at 16:02
  • @AnudeepAnanth with your code we can't move cursor to anywhere like default behavior, we get problem while edit the text – Mohd Qasim Mar 15 '22 at 13:00
  • to fix that we check my answer below – Mohd Qasim Mar 15 '22 at 13:19
36

You can use the FocusRequester with a SideEffect.
Something like:

var text by remember { mutableStateOf("text") }

// initialize focus reference to be able to request focus programmatically
val focusRequester = FocusRequester()

Column {
    TextField(
        value = text,
        onValueChange = {
            text = it
        },
        label = { Text("label") },
        modifier = Modifier
            // add focusRequester modifier 
            .focusRequester(focusRequester)
    )

Then use focusRequester.requestFocus(). In this way the system grants focus to the component associated with this FocusRequester.

To have the field automatically focused when the screen appears you can use:

LaunchedEffect(Unit) {
    focusRequester.requestFocus()
}

To manually grant the focus to the field:

    Button(
         onClick = { focusRequester.requestFocus() }
    ){
        Text("Click to give the focus")
    }
Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • 10
    What is the advantage of using ```DisposableEffect``` over ```LaunchedEffect``` in automatically focusing the field? – danartillaga Oct 08 '21 at 13:04
  • 1
    Or just `SideEffect` which doesn't launch a coroutine or needs to implement `onDispose`? – Florian Walther Oct 14 '21 at 07:50
  • 5
    DisposableEffect is used To request focus only once but it has no meaning since we don't use onDispose. so i think using LaunchedEffect(Unit){} make more sense because it will be launched only once, first time compose is created, and will not request focus on recomposition. – Amr Oct 14 '21 at 10:50
  • and how can I withdraw focus?? – Tanjim ahmed Dec 13 '21 at 13:19
  • 1
    I occasionally get a "FocusRequester is not initialized" exception when calling `focusRequester.requestFocus()` in a `LaunchedEffect` on some devices. Might be a race condition, but this solution does not work in all cases. – Sven Jacobs Jan 31 '22 at 08:00
  • 8
    @SvenJacobs Yes, this happens not only with ```LaucnhedEffect``` but also with ```DisposableEffect```. You should not request focus during composition. The solution I have found is to request a focus right after the composition. So, you will do ```LaunchedEffect(Unit) { this.coroutineContext.job.invokeOnCompletion { focusRequester.requestFocus() } }``` This makes sure your code will run when the ```LauchedEffect``` leaves the composition because it(the coroutine) either got canceled or completed. – Benyam Jan 31 '22 at 15:08
14
val focusRequester = remember { FocusRequester() }
val inputService = LocalTextInputService.current
val focus = remember { mutableStateOf(false) }
BasicTextField(
    value = "value",
    modifier = Modifier
        .height(40.dp)
        .fillMaxWidth()
        .focusRequester(focusRequester)
        .onFocusChanged {
            if (focus.value != it.isFocused) {
                focus.value = it.isFocused
                if (!it.isFocused) {
                    inputService?.hideSoftwareKeyboard()
                }
            }
        },
),

LaunchedEffect("") {
    delay(300)
    inputService?.showSoftwareKeyboard()
    focusRequester.requestFocus()
}
Abhimanyu
  • 11,351
  • 7
  • 51
  • 121
toutoumu
  • 157
  • 1
  • 2
  • 3
    Please provide additional details in your answer. As it's currently written, it's hard to understand your solution. – Community Sep 09 '21 at 07:01
  • Thanks for addressing the keyboard state! The other answers don't appear to do that. – VIN Mar 27 '23 at 16:39
12

In the latest version of Jetpack Compose (1.3.3 for now), if you use this:

LaunchedEffect(Unit) {
    focusRequester.requestFocus()
}

trying to auto focus a TextField, you would get:

java.lang.IllegalStateException:
FocusRequester is not initialized. Here are some possible fixes:

    1. Remember the FocusRequester: val focusRequester = remember { FocusRequester() }
    2. Did you forget to add a Modifier.focusRequester() ?
    3. Are you attempting to request focus during composition? Focus requests should be made in
    response to some event. Eg Modifier.clickable { focusRequester.requestFocus() }

Here is an elegant solution without using delay:

val focusRequester = remember { FocusRequester() }
var textFieldLoaded by remember { mutableStateOf(false) }

TextField(
    modifier = Modifier
        .focusRequester(focusRequester)
        .onGloballyPositioned {
            if (!textFieldLoaded) {
                focusRequester.requestFocus() // IMPORTANT
                textFieldLoaded = ture // stop cyclic recompositions
            }
        },
    value = someValue,
    onValueChange = {
        // ...
    }
)

// We don't need this
// LaunchedEffect(Unit) {
//     focusRequester.requestFocus()
// }

thetaprime
  • 356
  • 4
  • 11
  • 2
    I tried a lot of things to request focus, this is the only thing that works! A lot of focus related functions seem to be deprecated already in Jetpack Compose, but not these functions. This even works for components that are not focusable out of the box like `Text()`, just add `.focusable()` right after `.focusRequester(focusRequester)`, the order of these functions is important! – Glenn85 Jan 17 '23 at 09:39
  • 1
    Any reason this isn't the answer? This is so much simpler. – Travis Griggs Mar 31 '23 at 00:06
  • I have two TextFields and I am unable to switch focus from the first to the second TextField when I use this approach. Any Workarounds? – Tonnie May 01 '23 at 17:34
  • 2
    @Tonnie using 'requestFocus' within 'onGloballyPositioned' causes cyclic recompositions, and you gain focus infinitely. Use 'LaunchedEffect' approach instead – UneXp Jun 19 '23 at 10:05
  • @UneXp nice tip, I will keep that in mind. – Tonnie Jun 19 '23 at 15:06
6

If you wanted your text field to be focused automatically when you navigate to the screen, you can use this. This allows you not to requestFocus during composition which is not something you should do. Otherwise, you will get IllegalStateException with the message "FocusRequester is not initialized" at times.

 @Composable fun YourComposable(){ 
   val focusRequester = remember {FocusRequester()}
    
        LaunchedEffect(Unit) {
            this.coroutineContext.job.invokeOnCompletion {
                focusRequester.requestFocus()
            }
        }
    
       OutlinedTextField( modifier = Modifier.focusRequester(focusRequester))
         
              
}

This is due to the coroutine inside LaucnhedEffeect will be canceled when the LaunchedEffect leaves the composition.

Benyam
  • 329
  • 4
  • 6
5

Taken from Compose Unit Tests and applied to TextField Link

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

Edit: Changed Box to Row because Box has been deprecated in '1.0.0-alpha04'

2jan222
  • 1,732
  • 2
  • 16
  • 29
3

    @Composable fun SomeComposable() {
        val focusRequester = remember { FocusRequester() }

    OutlinedTextField(
        value = TextFieldValue(
        text = state.text, // state come from else where
        selection = TextRange(state.text.length,state.text.length)),  
   
        onValueChange = { _:TextFieldValue -> Unit },
        modifier = Modifier
          .fillMaxWidth()
          .focusRequester(focusRequester),)

    SideEffect {
        focusRequester.requestFocus()
        }
    }

Why SideEffect: the very first line of the docs.
For the Cursor Position this answer helped. onValueChanged is left as is in my working code, I have no need for it.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
dobhareach
  • 176
  • 3
  • 6