How to show keyboard with Jetpack Compose?
P

7

20

How can I slide in the keyboard? I tried:

val keyboardController: SoftwareKeyboardController? = LocalSoftwareKeyboardController.current
  keyboardController?.show()

But it does not work. What am I missing? Maybe some Manifest flags?

Pico answered 9/3, 2022 at 16:14 Comment(0)
J
27

To show keyboard in Compose:

val showKeyboard = remember { mutableStateOf(true) }
val focusRequester = remember { FocusRequester() }
val keyboard = LocalSoftwareKeyboardController.current

OutlinedTextField(
    modifier = Modifier
            .fillMaxWidth()
            .focusRequester(focusRequester),
    value = value,
    textStyle = MaterialTheme.typography.body2,
    onValueChange = { onValueChange(it)},
    label = { Text(label) }
)

// LaunchedEffect prevents endless focus request
LaunchedEffect(focusRequester) {
    if (showKeyboard.equals(true)) {
        focusRequester.requestFocus()
        delay(100) // Make sure you have delay here
        keyboard?.show()
    }
}
Josefina answered 25/5, 2022 at 9:19 Comment(6)
This code does not work when you open a dialog and want the textfield in dialog to be auto focused and keyboard slide open. The textfield focuses, but the keyboard doesn't slide open.Fredkin
@Fredkin See here for dialogs. Just move the delay before the requestFocus(), or use awaitFrame(). #69750947Ozonize
showKeyboard.equals(true) always returns false here because equals() Indicates whether one other object is "equal to" the other one not if they have the same value. See examples here - educative.io/answers/what-is-objectsequals-in-java , instead just use if(showKeyboard.value)Chesty
should be: if (showKeyboard.value) {...Arvillaarvin
Anyway the code never sets showKeyboard.value to false. So why is the variable there?Marjorymarjy
Never anywhere rely on delay. awaitFrame() is enoughAntonina
S
18

delay is not a robust way to show a keyboard. Keyboard is not shown because the window is not focused yet in most cases. The solution would be like this:

val windowInfo = LocalWindowInfo.current
val focusRequester = remember { FocusRequester() }
TextField(modifier = Modifier.focusRequester(focusRequester)...)

LaunchedEffect(windowInfo) {
     snapshotFlow { windowInfo.isWindowFocused }.collect { isWindowFocused ->
         if (isWindowFocused) {
             focusRequester.requestFocus()
         }
     }
}
Substantial answered 11/4, 2023 at 10:57 Comment(3)
Shouldn't we use request focus as a side effect?Emmalynne
Tried the awaitFrame and it seems to reduce the error rate of failed kbd hidings to something around 10%. But with your approach I can't get even a single failure! I guess awaitFrame sometimes is too short period for the window to become in focus. I wonder do we really need snapshotFlow? Can't we just access isWindowFocused directly?Scotney
Why do you need the snapshotFlow?Hematuria
C
11

I am currently using the compose BOM version 2023.05.01.

The other answers did not work for me. However, I did manage to get it to work when I changed the LaunchedEffect from the accepted answer. With the following code snippet, the software keyboard opens and you can start typing inside of the textfield.

val focusRequester = remember { FocusRequester() }

OutlinedTextField(
    modifier = Modifier
            .fillMaxWidth()
            .focusRequester(focusRequester),
    value = value,
    textStyle = MaterialTheme.typography.body2,
    onValueChange = { onValueChange(it)},
    label = { Text(label) }
)

LaunchedEffect(focusRequester) {
   awaitFrame()
   focusRequester.requestFocus()
}
Consummation answered 24/5, 2023 at 8:53 Comment(1)
This one seems to work best, it survives screen rotations and also works if the textfield is not empty.Crump
C
2

For my use case, I needed the keyboard to be shown when there is a bottom sheet shown with the editText. The accepted answer works for the first time and then stops working if the user switches to a different app for some time (interruptions). So I combined the above solution with a lifecycle event to trigger the keyboard when the view is actually visible to the user.

fun SomeView(lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current) {
 val focusRequester = remember { FocusRequester() }
 val keyboard = LocalSoftwareKeyboardController.current
 val scope = rememberCoroutineScope()


 DisposableEffect(key1 = lifecycleOwner, effect = {

       val observer = LifecycleEventObserver { _, event ->

           scope.launch {
               if (event == Lifecycle.Event.ON_RESUME) {
                   focusRequester.requestFocus()
                   awaitFrame()
                   keyboard?.show()
               }
           }
       }

       // Add the observer to the lifecycle
       lifecycleOwner.lifecycle.addObserver(observer)

       onDispose {
           lifecycleOwner.lifecycle.removeObserver(observer)
       }
   })


         TextField(
            modifier = Modifier
                .fillMaxWidth()
                .focusRequester(focusRequester),
            value = "",
            onValueChange = {},
            placeholder = { Text(text = "New Note") },
            colors = TextFieldDefaults.textFieldColors(
                backgroundColor = Color.Transparent,
            )
        )

The above adds a lifecycle observer and triggers the Keyboard show when the lifecycle event is ON_RESUME.

Cassareep answered 27/2, 2023 at 5:59 Comment(0)
Y
1

The following works fine when it is needed to show keyboard for an edit when scene is opened:

    val focusRequester = remember { FocusRequester() }
    LaunchedEffect(Unit) { focusRequester.requestFocus() }

    TextField(
        ...
        modifier = Modifier
           .fillMaxWidth()
           .focusRequester(focusRequester),
    )
Yokum answered 1/8, 2023 at 7:3 Comment(0)
E
-1

The answer from Leon Wu https://mcmap.net/q/613640/-how-to-show-keyboard-with-jetpack-compose seems to be the best one. For my use case, it was better to put request focus in a side effect.

val focusRequester = remember {
    FocusRequester()
}
var queryText by remember(query) { mutableStateOf(query) }

val scope = rememberCoroutineScope()
val windowInfo = LocalWindowInfo.current

SideEffect {
    scope.launch {
        snapshotFlow { windowInfo.isWindowFocused }.collect { isWindowFocused ->
            if (isWindowFocused && requestFocus) {
                focusRequester.requestFocus()
            }
        }
    }
}
TextField(modifier = Modifier.focusRequester(focusRequester)...)
Emmalynne answered 22/4, 2023 at 3:17 Comment(5)
Your answer does not compile.Callery
@J.Doe The same code compiles for me.Emmalynne
Nonsense, you are referring to a variable query which isn't defined anywhereCallery
The answer is not on how to use query. Its on how to show the keyboard. This is just code snippet not the whole code.Emmalynne
Useless, whats the point adding unrelated non-compiling code snippets? It makes no senseCallery
M
-12

Here's a quick hack using the View-Based Official Method but we use Compose instead by using the Interoperability API.

fun showSoftKeyboard(view: View) {
    if (view.requestFocus()) {
        val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
    }
}

Read more here.

And Passing an empty Composable (View) as the parameter:

showSoftKeyboard(AndroidView(context))
Mccarty answered 9/3, 2022 at 17:59 Comment(3)
This worked, but since I work with Jetpack Compose only, I don't like the view in the show-Method.Pico
That's nothing, just pass an AndroidView Composable to it and it'll do. You can just create an empty AndroidView for the purpose. This seems really simplistic so i find it ideal.Mccarty
Do not include meta-commentary in your answer. This is not the place for it. If you want to ask what is wrong with this approach, ask a new question. If you want to know how to improve this question, you can ask on Meta. Asking your readers what's wrong is not appropriate.Minuet

© 2022 - 2024 — McMap. All rights reserved.