16

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?

Ralf Wickum
  • 2,850
  • 9
  • 55
  • 103

7 Answers7

21

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()
    }
}
wbk727
  • 8,017
  • 12
  • 61
  • 125
adwardwo1f
  • 817
  • 6
  • 18
  • 7
    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. – ino Aug 28 '22 at 20:49
  • 1
    @ino See here for dialogs. Just move the delay before the `requestFocus()`, or use `awaitFrame()`. https://stackoverflow.com/questions/69750447/jetpack-compose-focus-requester-not-working-with-dialog – rileyx Nov 13 '22 at 15:17
  • 3
    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 - https://www.educative.io/answers/what-is-objectsequals-in-java , instead just use if(showKeyboard.value) – Itay Feldman Mar 15 '23 at 23:49
  • should be: if (showKeyboard.value) {... – Dan Alboteanu Jul 27 '23 at 07:40
  • Anyway the code never sets showKeyboard.value to false. So why is the variable there? – Pointer Null Sep 02 '23 at 10:55
10

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()
         }
     }
}
Leon Wu
  • 321
  • 3
  • 5
3

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()
}
Macksly
  • 141
  • 5
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.

Bawender Yandra
  • 200
  • 1
  • 9
0

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),
    )
RusArtM
  • 1,116
  • 3
  • 15
  • 22
-1

The answer from Leon Wu https://stackoverflow.com/a/75985103/4908512 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)...)
VIVEK CHOUDHARY
  • 468
  • 5
  • 8
-10

What's the issue with the official method?

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.

This method works if you just use it like this:

showSoftKeyboard(AndroidView(context))

You could also try eliminating the parameter entirely by placing the AndroidView in the function's body instead.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42
  • This worked, but since I work with Jetpack Compose only, I don't like the view in the show-Method. – Ralf Wickum Mar 15 '22 at 19:55
  • 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. – Richard Onslow Roper Apr 13 '22 at 23:14
  • 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. – Dharman Feb 07 '23 at 12:14