Search code examples
androidkotlinandroid-jetpack-composeandroid-compose-textfieldandroid-compose-exposeddropdown

Exposed drop-down menu for jetpack compose


I was wondering if there is a solution for Exposed drop-down menu for jetpack compose? I couldn't find a proper solution for this component inside jetpack compose. Any help?

Drop-down


Solution

  • The M2 (starting from the version 1.1.0-alpha06) and M3 have the implementation of ExposedDropdownMenu based on ExposedDropdownMenuBox with TextField and DropdownMenu inside.

    Something like:

        val options = listOf("Option 1", "Option 2", "Option 3", "Option 4", "Option 5")
        var expanded by remember { mutableStateOf(false) }
        var selectedOptionText by remember { mutableStateOf(options[0]) }
        
        ExposedDropdownMenuBox(
            expanded = expanded,
            onExpandedChange = {
                expanded = !expanded
            }
        ) {
            TextField(
                readOnly = true,
                value = selectedOptionText,
                onValueChange = { },
                label = { Text("Label") },
                trailingIcon = {
                    ExposedDropdownMenuDefaults.TrailingIcon(
                        expanded = expanded
                    )
                },
                colors = ExposedDropdownMenuDefaults.textFieldColors()
            )
            ExposedDropdownMenu(
                expanded = expanded,
                onDismissRequest = {
                    expanded = false
                }
            ) {
                options.forEach { selectionOption ->
                    DropdownMenuItem(
                        onClick = {
                            selectedOptionText = selectionOption
                            expanded = false
                        }
                    ){
                        Text(text = selectionOption) 
                    }
                }
            }
        }
    

    enter image description here

    If you are using M3 (androidx.compose.material3) you have also to pass the menuAnchor modifier to the TextField:

    ExposedDropdownMenuBox(
        expanded = expanded,
        onExpandedChange = { expanded = !expanded },
    ) {
       TextField(
            //...
            modifier = Modifier.menuAnchor()
        )
        ExposedDropdownMenu(){ /*..  */ }
    }
    

    Also in M3 in the DropdownMenuItem you have to move the content in the text parameter:

    DropdownMenuItem(
        text = { Text(text = selectionOption) },
        onClick = {
            selectedOptionText = selectionOption
            expanded = false
        }
    )
    

    With the M2 version 1.0.x there isn't a built-in component.
    You can use a OutlinedTextField + DropdownMenu. It is important to wrap the them in a Box. In this way the TextField will be used as the 'anchor'.

    It is just a basic (very basic) implementation:

    var expanded by remember { mutableStateOf(false) }
    val suggestions = listOf("Item1","Item2","Item3")
    var selectedText by remember { mutableStateOf("") }
    
    var textfieldSize by remember { mutableStateOf(Size.Zero)}
    
    val icon = if (expanded)
        Icons.Filled.ArrowDropUp //it requires androidx.compose.material:material-icons-extended
    else
        Icons.Filled.ArrowDropDown
    
    
    Box() {
        OutlinedTextField(
            value = selectedText,
            onValueChange = { selectedText = it },
            modifier = Modifier
                .fillMaxWidth()
                .onGloballyPositioned { coordinates ->
                    //This value is used to assign to the DropDown the same width
                    textfieldSize = coordinates.size.toSize()
                },
            label = {Text("Label")},
            trailingIcon = {
                Icon(icon,"contentDescription",
                     Modifier.clickable { expanded = !expanded })
            }
        )
        DropdownMenu(
            expanded = expanded,
            onDismissRequest = { expanded = false },
            modifier = Modifier
                .width(with(LocalDensity.current){textfieldSize.width.toDp()})
        ) {
            suggestions.forEach { label ->
                DropdownMenuItem(onClick = {
                    selectedText = label
                }) {
                    Text(text = label)
                }
            }
        }
    }
    

    enter image description here enter image description here