Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetpack Compose: Textfield doesn't lose focus on click outside of it

I am trying to implement a searchbar in my Compose app. I have a textfield, which I want to lose focus of and hide the keyboard, when I click outside of the textfield. However, I can't figure out how to do it. I did try to do it this way, which I saw on GitHub.

However, I did some changes. The original solution used this part of code isHintDisplayed = it != FocusState.Active which seem to be deprecated. So I changed it to isHintDisplayed = it.isFocused != true which should in theory do the same. If I revert back to older versions of compose and use FocusState.Active, the focus/unfocus works perfectly, but I can't get it to work with my code.

Any ideas?

Here is my code:

@Composable
fun ListScreen(
    navController: NavController
) {
    Surface(
        color = MaterialTheme.colors.background,
        modifier = Modifier.fillMaxSize()
    ) {
        Column {
            Spacer(modifier = Modifier.height(20.dp))
            Image(
                painter = painterResource(id = R.drawable.ic_international_pok_mon_logo),
                contentDescription = "Cards",
                modifier = Modifier
                    .fillMaxWidth()
                    .align(CenterHorizontally)
            )
            SearchBar(
                hint = "Search",
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
            ) {

            }
        }
    }
}

And here is Searchbar:

@Composable
fun SearchBar(
    modifier: Modifier = Modifier,
    hint: String = "",
    onSearch: (String) -> Unit = {}
) {
    var text by remember {
        mutableStateOf("")
    }
    var isHintDisplayed by remember {
        mutableStateOf(false)
    }

    Box(modifier = modifier) {
        BasicTextField(
            value = text,
            onValueChange = {
                text = it
                onSearch(it)
            },
            maxLines = 1,
            singleLine = true,
            textStyle = TextStyle(color = Color.Black),
            modifier = Modifier
                .fillMaxWidth()
                .shadow(5.dp, CircleShape)
                .background(Color.White, CircleShape)
                .padding(horizontal = 20.dp, vertical = 12.dp)
                .onFocusChanged {
                    isHintDisplayed = !it.isFocused
                }
        )
        if(isHintDisplayed) {
            Text(
                text = hint,
                color = Color.LightGray,
                modifier = Modifier
                    .padding(horizontal = 20.dp, vertical = 12.dp)
            )
        }
    }
}
like image 945
CJR Avatar asked Nov 29 '25 15:11

CJR


1 Answers

If you want to hide keyboard and want to clear focus, you can do it by adding KeyboardOptions and KeyboardActions in your TextField. You can do it by adding any ImeAction you want, e.g. ImeAction.Search.
If You want to hide keyboard on outside area click, then you can set a clickable modifier in your parent composable, and manage keyboard show hide state by a variable, state of which will be changed upon clicking parent composable.
If you want to manage keyboard display option and focus state, by both keyboard action and outside area click, you can modify your code as below.

@Composable
fun ListScreen() {
    Surface(
        color = MaterialTheme.colors.background,
        modifier = Modifier.fillMaxSize()
    ) {
        var hideKeyboard by remember { mutableStateOf(false) }
        Column(modifier = Modifier.clickable { hideKeyboard = true }) {
            Spacer(modifier = Modifier.height(20.dp))
            Image(
                painter = painterResource(id = R.drawable.ic_launcher_foreground),
                contentDescription = "Cards",
                modifier = Modifier
                    .fillMaxWidth()
                    .align(CenterHorizontally)
            )
            SearchBar(
                hint = "Search",
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp),
                hideKeyboard = hideKeyboard,
                onFocusClear = { hideKeyboard = false }
            ) {

            }
        }
    }
}


@Composable
fun SearchBar(
    modifier: Modifier = Modifier,
    hint: String = "",
    hideKeyboard: Boolean = false,
    onFocusClear: () -> Unit = {},
    onSearch: (String) -> Unit = {}
) {
    var text by remember {
        mutableStateOf("")
    }
    var isHintDisplayed by remember {
        mutableStateOf(false)
    }
    val focusManager = LocalFocusManager.current

    Box(modifier = modifier.background(color = Color.Red)) {
        BasicTextField(
            value = text,
            onValueChange = {
                text = it
                onSearch(it)
            },
            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
            keyboardActions = KeyboardActions(onSearch = {
                focusManager.clearFocus()
                onSearch(text)
            }),
            maxLines = 1,
            singleLine = true,
            textStyle = TextStyle(color = Color.Black),
            modifier = Modifier
                .fillMaxWidth()
                .shadow(5.dp, CircleShape)
                .background(Color.White, CircleShape)
                .padding(horizontal = 20.dp, vertical = 12.dp)
                .onFocusChanged {
                    isHintDisplayed = !it.hasFocus
                }
        )
        if(isHintDisplayed) {
            Text(
                text = hint,
                color = Color.LightGray,
                modifier = Modifier
                    .padding(horizontal = 20.dp, vertical = 12.dp)
            )
        }
    }

    if (hideKeyboard) {
        focusManager.clearFocus()
        // Call onFocusClear to reset hideKeyboard state to false
        onFocusClear()
    }
}
like image 187
Ahsan Ullah Rasel Avatar answered Dec 02 '25 04:12

Ahsan Ullah Rasel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!