How to close the virtual keyboard from a Jetpack Compose TextField?
H

10

123

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 }
)
Hypnosis answered 2/12, 2019 at 5:25 Comment(0)
D
112

Starting from compose 1.0.0-alpha12 (and still valid in compose 1.6.4) 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.

Domestic answered 18/2, 2021 at 11:33 Comment(5)
This is not working inside an AlertDialog. Nothing I have tried, SoftwareKeyboardController, FocusManager, nor FocusRequester work in a dialog. Any ideas?Shrieve
@Shrieve Try to initialize FocusManager inside the dialog. In my case it worked.Agueda
@Agueda yes, this is it! Thanks! Maybe post it as a separate answer too.Shrieve
@Shrieve Did you get any solution for this on dialog? I am facing the same issue with dialog.Avelar
@Avelar yes, the comment above, from Drogheda solved it!Shrieve
T
256

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.

Tabanid answered 1/9, 2020 at 22:3 Comment(6)
What are the differences between this and the focusManager approach (https://mcmap.net/q/180237/-how-to-close-the-virtual-keyboard-from-a-jetpack-compose-textfield)? When should we use each?Learning
@Learning 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.)Redmer
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.Redmer
@Learning for additional details, see my answer below https://mcmap.net/q/180237/-how-to-close-the-virtual-keyboard-from-a-jetpack-compose-textfieldRedmer
@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.Landa
It's funny also sad that LocalSoftwareKeyboardController is Experimental since 2020!Drily
D
112

Starting from compose 1.0.0-alpha12 (and still valid in compose 1.6.4) 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.

Domestic answered 18/2, 2021 at 11:33 Comment(5)
This is not working inside an AlertDialog. Nothing I have tried, SoftwareKeyboardController, FocusManager, nor FocusRequester work in a dialog. Any ideas?Shrieve
@Shrieve Try to initialize FocusManager inside the dialog. In my case it worked.Agueda
@Agueda yes, this is it! Thanks! Maybe post it as a separate answer too.Shrieve
@Shrieve Did you get any solution for this on dialog? I am facing the same issue with dialog.Avelar
@Avelar yes, the comment above, from Drogheda solved it!Shrieve
R
25

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.

Redmer answered 4/8, 2021 at 10:55 Comment(0)
B
11

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()
                }
        ...
Briny answered 4/8, 2021 at 7:48 Comment(1)
How can we SHOW the keyboard when we click the button? (keyboardController?.show() isn't working in this case)Antennule
D
5

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()
})
Delora answered 8/6, 2023 at 3:53 Comment(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.Root
H
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 }
)
Hypnosis answered 2/12, 2019 at 12:27 Comment(0)
S
2

Here is Modifer extension to hide keyboard:

fun Modifier.hideKeyboardOnOutsideClick(): Modifier = composed {
    val controller = LocalSoftwareKeyboardController.current
    this then Modifier.noRippleClickable {
         controller?.hide()
    }
}

To avoid a ripple effect on the background use this extension:

fun Modifier.noRippleClickable(onClick: () -> Unit): Modifier = composed {
    this then Modifier.clickable(
         indication = null,
         interactionSource = remember { MutableInteractionSource() },
         onClick = onClick
    )
}

Usage:

setContent {
    // Main Container with keyboard hiding behavior
    Box(
        modifier = Modifier
            .fillMaxSize()
            .hideKeyboardOnOutsideClick()
    ) {
        // Your app's content here
    }
}
Suttee answered 5/2 at 8:50 Comment(0)
U
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
)
Upshaw answered 22/3, 2022 at 1:6 Comment(2)
See "Explaining entirely code-based answers". 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.Graces
Even this is not working in an AlertDialog. Any ideas?Shrieve
C
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

Carven answered 4/1, 2022 at 3:17 Comment(0)
R
0

To achieve this, you can add the following code to your text field:

... singleLine = true,

By doing this, the Enter button will be converted to a Done button and the keyboard will be closed when clicked on.

sample code:

TextField(
                    value = myUserTyped,
                    singleLine = true,
Rotherham answered 7/3 at 21:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.