105

I'm using the Jetpack Compose TextField and I want to close the virtual keyboard when the user press the the action button (imeActionPerformed parameter).

val text = +state { "" }
TextField(
    value = text.value,
    keyboardType = KeyboardType.Text,
    imeAction = ImeAction.Done,
    onImeActionPerformed = { 
        // TODO Close the virtual keyboard here <<<
    }
    onValueChange = { s -> text.value = s }
)
azizbekian
  • 60,783
  • 13
  • 169
  • 249
nglauber
  • 18,674
  • 6
  • 70
  • 75

8 Answers8

228

You can use the LocalSoftwareKeyboardController class to control the current software keyboard and then use the hide method:

var text by remember { mutableStateOf(TextFieldValue("Text")) }
val keyboardController = LocalSoftwareKeyboardController.current

TextField(
        value = text,
        onValueChange = {
            text = it
        },
        label = { Text("Label") },
        keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
        keyboardActions = KeyboardActions(
                onDone = {keyboardController?.hide()})
)

This solution closes the keyboard without removing the focus from the current TextField.

Just to highlight the difference with:

val focusManager = LocalFocusManager.current
focusManager.clearFocus()

This code closes the keyboard removing the focus from the TextField.

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • 73
    This is every Android Developer's dream – Andrew Chelix Apr 14 '21 at 10:29
  • 3
    What are the differences between this and the focusManager approach (https://stackoverflow.com/a/66259111/2911458)? When should we use each? – stkent May 22 '21 at 23:10
  • 2
    @stkent this solution only hides the keyboard. The other solution also removes the focus from the current TextField (the cursor is still showing, it might still be highlighted, etc.) – m.reiter Aug 04 '21 at 09:25
  • 1
    When choosing between both solutions please also watch ux-continuity: The default-behavior: swiping back once hides the keyboard, and second swipe clears the focus. – m.reiter Aug 04 '21 at 09:31
  • Thanks, @m.reiter! – stkent Aug 04 '21 at 14:18
  • 1
    @stkent for additional details, see my answer below https://stackoverflow.com/a/68650009/12871582 – m.reiter Aug 04 '21 at 14:58
  • @Gabriele Mariotti How can I set up a TextField to **never invoking** the virtual keyboard at all? [Click on here to go to that question of mine.](https://stackoverflow.com/questions/71535062/how-to-disable-showing-the-virtual-keyboard-when-tapping-on-a-textfield) – Jorge Luiz Mar 20 '22 at 03:52
  • It's funny also sad that `LocalSoftwareKeyboardController` is Experimental since 2020! – YaMiN Jul 10 '22 at 17:13
101

Starting from compose 1.0.0-alpha12 (and still valid in compose 1.4.3) the onImeActionPerformed is deprecated and suggested approach is to use keyboardActions with combination of keyboardOptions:

    val focusManager = LocalFocusManager.current

    OutlinedTextField(
        value = ...,
        onValueChange = ...,
        label = ...,
        keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
        keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done, keyboardType = KeyboardType.Password),
    )

focusManager.clearFocus() will take care of dismissing the soft keyboard.

azizbekian
  • 60,783
  • 13
  • 169
  • 249
21

In 1.0.0 you can either use SoftwareKeyboardController or FocusManager to do this.

This answer focuses on their differences.


Setup:

var text by remember { mutableStateOf("")}

TextField(
    value = text,
    onValueChange = { text = it },
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
    keyboardActions = KeyboardActions(onDone = { /* TODO */ }),
)

setup


SoftwareKeyboardController:

Based on @Gabriele Mariottis answer.

val keyboardController = LocalSoftwareKeyboardController.current

// TODO =
keyboardController?.hide()

This only closes the keyboard, but does NOT clear the focus from any focused TextField (note the cursor & thick underline).

Using Keyboard Controller


FocusManager:

Based on @azizbekians answer.

val focusManager = LocalFocusManager.current

// TODO =
focusManager.clearFocus()

Using Focus Manager

This closes the keyboard AND clears the focus from the TextField.

m.reiter
  • 1,796
  • 2
  • 11
  • 31
8

Hiding the keyboard on button click

To add with Gabriele Mariotti's solution, if you want to hide the keyboard conditionally, say after a button click, use this:

keyboardController?.hide()

For example, hide the keyboard after clicking the Add button:

var newWord by remember { mutableStateOf("") }
val keyboardController = LocalSoftwareKeyboardController.current

// Setup the text field with keyboard as provided by Gabriele Mariotti

...

Button(
        modifier = Modifier
                .height(56.dp),
        onClick = {
                if (!newWord.trim().isNullOrEmpty()) {
                        wordViewModel.onAddWord(newWord.trim())
                        newWord = ""
                        keyboardController?.hide()
                }
        ...
Hasan
  • 589
  • 2
  • 6
  • 11
4

Edit after alpha-12 release: See @azizbekian response.

Pre-alpha-12 response

I found the solution here :)

fun hideKeyboard(activity: Activity) {
    val imm: InputMethodManager = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    var view = activity.currentFocus
    if (view == null) {
        view = View(activity)
    }
    imm.hideSoftInputFromWindow(view.windowToken, 0)
}

I just need to call the function above from my component:

// getting the context
val context = +ambient(ContextAmbient)

// textfield state
val text = +state { "" }

TextField(
    value = text.value,
    keyboardType = KeyboardType.Text,
    imeAction = ImeAction.Done,
    onImeActionPerformed = { 
        if (imeAction == ImeAction.Done) {
            hideKeyboard(context as Activity)
        }
    }
    onValueChange = { s -> text.value = s }
)
nglauber
  • 18,674
  • 6
  • 70
  • 75
3

There are two scenarios

Scenario 1 - A TextField in an AlertDialog

Make sure to initialize the keyboard controller or focus manager inside the Dialog content scope

(Dialog has its own keyboard controller)

Dialog(
    onDismissRequest = {
       // on dismiss
    }
) {
   // initialise the keyboard controller and focus Manager inside the content scope
   val keyboardController = LocalSoftwareKeyboardController.current
   val focusManager = LocalFocusManager.current
}

hide the keyboard

keyboardController?.hide()
focusManager.clear()

Edge case - Dismiss the dialog and hide the keyboard

There might be a keyboard flickering (keyboard hide and show quickly) issue when dismissing the dialog and keyboard at the same time.

Try to hide the keyboard first then trigger the dismiss dialog event with a delay

Dialog() {
    val keyboardController = LocalSoftwareKeyboardController.current
    val focusManager = LocalFocusManager.current

    Button(onClick = {
       keyboardController?.hide()
       focusManager.clear()

       // notify the view model to dismiss the dialog

       viewModel.onNegativeButtonClicked()
    })
}

Inside the ViewModel

ViewModel {
    
  fun onNegativeButtonClicked() {

     // trigger dismissDialog event with delay
  }

}

Scenario 2 - TextField only (no dialog)

val keyboardController = LocalSoftwareKeyboardController.current
var text by rememberSaveable { mutableStateOf("") }
TextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Label") },
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
    keyboardActions = KeyboardActions(
        onDone = {
            keyboardController?.hide()
            // do something here
        }
    )
)

or you can just use keyboardController focusManager to hide the keyboard inside the click event

Button(onClick = {
    keyboardController?.hide()
})
Jeffery Ma
  • 3,051
  • 1
  • 23
  • 26
  • 1
    "Make sure to initialize the keyboard controller or focus manager inside the Dialog scope (Dialog has its own keyboard controller)" thank you, I wasted an hour on this. – stijndcl Jun 25 '23 at 20:10
1

I found a way to shut him down in the CoreTextField,use TextInputService to control the switch

val focus = LocalTextInputService.current
var text by remember{ mutableStateOf("")}
TextField(
    value = text,
    onValueChange = { text = it },
    keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done, keyboardType = KeyboardType.Text),
    keyboardActions = KeyboardActions(onDone = { focus?.hideSoftwareKeyboard() }),
    singleLine = true
)
Yshh
  • 654
  • 1
  • 10
  • See "[Explaining entirely code-based answers](https://meta.stackoverflow.com/q/392712/128421)". While this might be technically correct, it doesn't explain why it solves the problem or should be the selected answer. We should educate along with helping solve the problem. – the Tin Man Mar 22 '22 at 04:48
  • Even this is not working in an `AlertDialog`. Any ideas? – shelll Apr 16 '23 at 16:04
0

implementation 'androidx.compose.material3:material3:1.0.0-alpha02'

Text Field With Hide Keyboard On Ime Action

@OptIn(ExperimentalComposeUiApi::class)
    @Composable
    fun TextFieldWithHideKeyboardOnImeAction() {
        val keyboardController = LocalSoftwareKeyboardController.current
        var text by rememberSaveable { mutableStateOf("") }
        TextField(
            value = text,
            onValueChange = { text = it },
            label = { Text("Label") },
            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
            keyboardActions = KeyboardActions(
                onDone = {
                    keyboardController?.hide()
                    // do something here
                }
            )
        )
    }

Text Field With Hide Keyboard On Ime Action

Bolt UIX
  • 5,988
  • 6
  • 31
  • 58