won
won

Reputation: 13

How to maintain keyboard focus with a searchable dropdown menu in Jetpack Compose?

I'm trying to implement a searchable dropdown menu in Jetpack Compose, but whenever the dropdown appears, the keyboard loses focus and disappears. How can I modify the code to prevent the keyboard from dismissing when the dropdown expands? Additionally, how can I ensure that the dropdown menu always remains anchored below the button? Here's the relevant part of my code:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SelectBox() {
    var expanded by remember { mutableStateOf(false) }
    var textInput by remember { mutableStateOf("") } // State to hold user input
    var selectedDepartment by remember { mutableStateOf<String?>(null) }
    val departments = DepartmentData.departments // Sample department data
    val focusRequester = remember { FocusRequester() }

    // Filtering departments based on user input
    val filteredDepartments = departments.filter {
        it.lowercase().contains(textInput.lowercase())
    }

    Box(modifier = Modifier
        .fillMaxWidth()
        .clickable { expanded = !expanded }) {
        OutlinedTextField(
            value = textInput,
            onValueChange = {
                textInput = it
                if (it.isNotEmpty()) expanded = true
            },
            label = { Text("Department") },
            modifier = Modifier
                .fillMaxWidth()
                .height(58.dp)
                .focusRequester(focusRequester),
            shape = RoundedCornerShape(8.dp),
            singleLine = true,
            trailingIcon = {
                Icon(
                    imageVector = if (expanded) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown,
                    contentDescription = "Dropdown toggle",
                    modifier = Modifier.clickable {
                        expanded = !expanded
                        focusRequester.requestFocus() // Maintain focus
                    }
                )
            },
            colors = TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color(0xFF153A99),
                focusedLabelColor = Color(0xFF153A99)
            )
        )

        DropdownMenu(
            expanded = expanded,
            onDismissRequest = {
                expanded = false
                focusRequester.requestFocus()
            },
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(max = 100.dp)
        ) {
            filteredDepartments.forEach { department ->
                DropdownMenuItem(
                    text = { Text(department) },
                    onClick = {
                        selectedDepartment = department
                        textInput = department
                        expanded = false
                        focusRequester.requestFocus()
                    }
                )
            }
        }
    }
}

Any suggestions on how to resolve the issue of the keyboard dismissing or how to anchor the dropdown properly would be greatly appreciated. Thank you!

I removed the clickable modifier inside the OutlinedTextField to prevent duplicate click events. I called focusRequester.requestFocus() in the click events for the DropdownMenu and the trailing icon to maintain keyboard focus whether the dropdown is expanded or collapsed.

Upvotes: 1

Views: 242

Answers (1)

Megh Lath
Megh Lath

Reputation: 2214

For the issue of keyboard beign dismissed, you can set focusable property as false in DropdownMenu. And in terms of anchoring dropdown below textfield, the current code will work fine unless you have kept this code inside Surface.

The below code works fine for me with proper anchor and keyboard case:


Box(modifier = Modifier.fillMaxWidth().clickable { expanded = !expanded }) {
    OutlinedTextField(
        value = textInput,
        onValueChange = {
            textInput = it
            expanded = true
        },
        label = { Text("Department") },
        modifier =
            Modifier.fillMaxWidth().height(58.dp).focusRequester(focusRequester).onFocusChanged {
                if (it.hasFocus) {
                    expanded = true
                }
            },
        shape = RoundedCornerShape(8.dp),
        singleLine = true,
        trailingIcon = {
            Icon(
                imageVector =
                    if (expanded) Icons.Default.KeyboardArrowUp
                    else Icons.Default.KeyboardArrowDown,
                contentDescription = "Dropdown toggle",
                modifier =
                    Modifier.clickable {
                        expanded = !expanded
                        focusRequester.requestFocus() // Maintain focus
                    }
            )
        },
        colors =
            TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color(0xFF153A99),
                focusedLabelColor = Color(0xFF153A99)
            )
    )

    DropdownMenu(
        expanded = expanded,
        onDismissRequest = { expanded = false },
        modifier = Modifier.fillMaxWidth(),
        properties = PopupProperties(focusable = false),
    ) {
        filteredDepartments.forEach { department ->
            DropdownMenuItem(
                text = { Text(department) },
                onClick = {
                    selectedDepartment = department
                    textInput = department
                    expanded = false
                }
            )
        }
    }
}

Upvotes: 0

Related Questions