Home > OS >  Jetpack compose: possible to set DropdownMenu height to display next item
Jetpack compose: possible to set DropdownMenu height to display next item

Time:11-01

I'm using the DropdownMenu in Jetpack Compose, but I've got a problem that a lot of users doesn't understand that the menu contains more information so they can scroll the list. I get a lot of support that they're missing things from the list. Especially in some languages it nicely lines up the hight with an item, so you don't see that there are more items.

Is there a way to automatically set the height of the dropdown menu (no matter the language) so it displays half of an item at the bottom, so users understand that they can scroll the list?

I create the menu using these modifiers:

DropdownMenu(
    expanded = expanded,
    onDismissRequest = { expanded = false },
    modifier = Modifier.requiredSizeIn(maxHeight = 330.dp)
) {
        items.forEachIndexed { index, item ->
                DropdownMenuItem(onClick = {
                    onSelected(index, item.data)
                    selectedIndex = index
                    expanded = false
                }) {
...
}

CodePudding user response:

I'm agree that this is a nice feature to have, I suggest you create a feature request on the Compose issue tracker.

Here's a workaround for now, you can get your item heights using Modifier.onSizeChanged and update your maxHeight constraint like this:

@Composable
fun TestScreen(
) {
    var expanded by remember { mutableStateOf(true) }
    val items = List(10) { it.toString() }
    val itemHeights = remember { mutableStateMapOf<Int, Int>() }
    val baseHeight = 330.dp
    val density = LocalDensity.current
    val maxHeight = remember(itemHeights.toMap()) {
        if (itemHeights.keys.toSet() != items.indices.toSet()) {
            // if we don't have all heights calculated yet, return default value
            return@remember baseHeight
        }
        val baseHeightInt = with(density) { baseHeight.toPx().toInt() }

        // top bottom system padding
        var sum = with(density) { DropdownMenuVerticalPadding.toPx().toInt() } * 2
        for ((i, itemSize) in itemHeights.toSortedMap()) {
            sum  = itemSize
            if (sum >= baseHeightInt) {
                return@remember with(density) { (sum - itemSize / 2).toDp() }
            }
        }
        // all items fit into base height
        baseHeight
    }

    DropdownMenu(
        expanded = expanded,
        onDismissRequest = { expanded = false },
        modifier = Modifier.requiredSizeIn(maxHeight = maxHeight)
    ) {
        items.forEachIndexed { index, item ->
            DropdownMenuItem(
                onClick = {
                    onSelected(index, item.data)
                    selectedIndex = index
                    expanded = false
                },
                modifier = Modifier.onSizeChanged {
                    itemHeights[index] = it.height
                }
            ) {
                Text("Hello $index", modifier = Modifier.padding(30.dp))
            }
        }
    }
}

private val DropdownMenuVerticalPadding = 8.dp

p.s. DropdownMenuVerticalPadding is a Material constant taken from the source code.

  • Related