132

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

7 Answers 7

176

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()
    }
}
Sign up to request clarification or add additional context in comments.

8 Comments

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?
@CharlesWoodson you'll need to use TextFieldValue in the TextField's value. You can set selection in TextFieldValue
@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.
Wouldn't LaunchedEffect be better to use instead of DisposableEffect?
Note that if the composable C you're requesting focus of is inside of another S which does subcomposition, like Scaffold or Dialog, the LaunchedEffect should always be called inside S, not outside. If called outside, you will get "FocusRequester is not initialized" errors in tests. Source: this WONTFIX response from a Google developer
|
46

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

7 Comments

What is the advantage of using DisposableEffect over LaunchedEffect in automatically focusing the field?
Or just SideEffect which doesn't launch a coroutine or needs to implement onDispose?
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.
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.
@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.
|
39

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 = true // stop cyclic recompositions
            }
        },
    value = someValue,
    onValueChange = {
        // ...
    }
)

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

4 Comments

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!
Any reason this isn't the answer? This is so much simpler.
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 using 'requestFocus' within 'onGloballyPositioned' causes cyclic recompositions, and you gain focus infinitely. Use 'LaunchedEffect' approach instead
17
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()
}

2 Comments

Please provide additional details in your answer. As it's currently written, it's hard to understand your solution.
Thanks for addressing the keyboard state! The other answers don't appear to do that.
9

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.

Comments

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
                },

                )
        }
        LaunchedEffect(Unit) {
           focusRequester.requestFocus()
        }
    }

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

Edit: onActive no longer exists use LaunchedEffect instead

3 Comments

What is onActive?
an old LaunchedEffect you can use this: LaunchedEffect(Unit) { focusRequester.requestFocus() }
FocusRequester() should be remembered!
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.

1 Comment

"A SideEffect runs after every recomposition."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.