android compose textfield how to dismiss keyboard on touch outside

Viewed 3522

Here is a Row that contains a TextField in a full-screen column. onFocusChanged callback is called when tapping the TextField (going Active) and when the Done button is pressed in the keyboard. But the handler is not called I tap outside the TextField. So I am not able to close the keyboard. Any idea?

val (nameText, setNameText) = remember { mutableStateOf("")}
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = FocusRequester()

Column(
    modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight()
        .padding(10.dp)
) { 
    Row(
        modifier = Modifier
            .fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceAround
    ) {
        TextField(
            value = nameText,
            onValueChange = setNameText,
            modifier = Modifier
                .focusRequester(focusRequester)
                .onFocusChanged {
                    if (!it.isFocused) {
                        keyboardController?.hide()
                    }
                }
                .padding(horizontal = 0.dp, vertical = 0.dp)
                .height(50.dp)
                .fillMaxWidth(),
            KeyboardOptions(
                keyboardType = KeyboardType.Text,
                imeAction = androidx.compose.ui.text.input.ImeAction.Done
            ),
            keyboardActions = KeyboardActions(onDone = {
                localFocusManager.clearFocus()
            }),
            placeholder = { Text("Enter a name", fontSize = 14.sp) }
        )
    }
}

I tried setting a focus handler in Column but did not work (focus handler not called when tapping outside). Using compose version 1.0.2.

1 Answers

If you want to remove focus on tap, you can add pointerInput modifier with detectTapGestures to your Column:

Column(
    modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight()
        .padding(10.dp)
        .pointerInput(Unit) {
            detectTapGestures(onTap = {
                localFocusManager.clearFocus()
            })
        }
)

I add it after the padding modifier, so the padding view part won't receive touches. If you change the order, padding will be included in the clickable part.

Look at other gestures if tap is not enough for you.

Related