compose-multiplatform
compose-multiplatform copied to clipboard
ExposedDropdownMenu can overlap the textfield
If the ExposedDropdownMenu is too tall, it will overlap the textfield:
@OptIn(ExperimentalMaterialApi::class)
fun main() = singleWindowApplication {
Column(Modifier.padding(start = 8.dp)) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it}
) {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
modifier = Modifier
.size(120.dp, 47.dp) // Height must be less than 48.dp to reproduce
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.exposedDropdownSize(true)
) {
repeat(100) {
DropdownMenuItem({}) { Text("Item $it") }
}
}
}
}
}
Affected platforms
- All
- Compose Multiplatform version: 1.5.10
The issue appears to be in the calculation of menuHeight
in ExposedDropdownMenuBox
:
modifier.onGloballyPositioned {
width = it.size.width
val boundsInWindow = it.boundsInWindow()
val visibleWindowBounds = windowInfo.containerSize.toIntRect()
val heightAbove = boundsInWindow.top - visibleWindowBounds.top
val heightBelow = visibleWindowBounds.height - boundsInWindow.bottom
menuHeight = max(heightAbove, heightBelow).toInt() - verticalMarginInPx
}
We're assuming the menu can overlap verticalMarginInPx
either at the top or the bottom of the window, but not both. But if the textfield is short enough and close enough to the top of the window, and the menu is tall enough, the "heightBelow" can overlap verticalMarginInPx
both at the top and the bottom of the window.
in Android code (androidx-main
tip; not yet released) this is done like so:
modifier.onGloballyPositioned {
anchorCoordinates = it
anchorWidth = it.size.width
menuMaxHeight = calculateMaxHeight(
windowBounds = view.rootView.getWindowBounds(),
anchorBounds = anchorCoordinates.getAnchorBounds(),
verticalMargin = verticalMargin,
)
}
private fun calculateMaxHeight(
windowBounds: Rect,
anchorBounds: Rect?,
verticalMargin: Int,
): Int {
anchorBounds ?: return 0
val marginedWindowTop = windowBounds.top + verticalMargin
val marginedWindowBottom = windowBounds.bottom - verticalMargin
val availableHeight =
if (anchorBounds.top > windowBounds.bottom || anchorBounds.bottom < windowBounds.top) {
(marginedWindowBottom - marginedWindowTop).roundToInt()
} else {
val heightAbove = anchorBounds.top - marginedWindowTop
val heightBelow = marginedWindowBottom - anchorBounds.bottom
max(heightAbove, heightBelow).roundToInt()
}
return max(availableHeight, 0)
}
any updates on this issue? Or maybe extended answer how to fix it by myself?
how to fix it
How can I apply the above workaround?
Same issue here. If there are more than a few items in the suggestions list, the whole ExposedDropdownMenu covers the TextField.
This is actually a problem in AOSP. I've reported the bug here: https://issuetracker.google.com/issues/360881340
The real issue is not in our menu height calculation. The dropdown menu should align to the bottom of the text field even if it means going inside the vertical margin. We could improve the situation somewhat by limiting the dropdown height to
visibleWindowBounds.height - 2*verticalMarginInPx
but then the dropdown will look like this:
Please comment here when someone knows how to fix this
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
providing max height can help
ExposedDropdownMenu(modifier = Modifier.heightIn(max = 200.dp) ....