I'm trying to use Jetpack Compose's ExposedDropdownMenuBox but I can't prevent it from showing the menu when scrolling.
For example, here's the code to replicate this problem:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
Surface(color = MaterialTheme.colors.background) {
Column(
modifier = Modifier
.padding(horizontal = 8.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
repeat(20){
ExposedDropdownMenuSample()
}
}
}
}
}
}
}
ExposedDropdownMenuSample was taken from the official samples.
This is a GIF showing the problem.
How can I prevent this from happening?
This code is using compose version 1.1.0-rc01.
I was able to fix this with this ugly hack:
private fun Modifier.cancelOnExpandedChangeIfScrolling() = pointerInput(Unit) {
forEachGesture {
coroutineScope {
awaitPointerEventScope {
var event: PointerEvent
var startPosition = Offset.Unspecified
var cancel = false
do {
event = awaitPointerEvent(PointerEventPass.Initial)
if (startPosition == Offset.Unspecified) {
startPosition = event.changes.first().position
}
val distance = startPosition.minus(event.changes.last().position).getDistance()
cancel = distance > 10f || cancel
} while (
!event.changes.all { it.changedToUp() }
)
if (cancel) {
event.changes.forEach { it.consumeAllChanges() }
}
}
}
}
}
then add it to the ExposedDropdownMenuBox like:
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
}, modifier = Modifier.cancelOnExpandedChangeIfScrolling()
)
So it basically checks if there was a drag for more than 10 pixels? and if true, consumes the events so it won't trigger the onExpandedChange.
10 is just a magic number that worked well for my tests, but it seems to be too low for a high res screen device. I'm sure there's a better way to calculate this number... Maybe someone more experienced can help with this until we have a proper fix?
I found a slightly less hacky workaround:
You get the info whether a scrolling is in progress from the scroll-state of the Column.
val scrollState = rememberScrollState()
val isScrolling = scrollState.isScrollInProgress
Column(
modifier = Modifier
.padding(horizontal = 8.dp)
.verticalScroll(scrollState),
...
) ...
In the ExposedDropdownMenuBox you can then change the listener to
onExpandedChange = {
expanded = !expanded && !isScrolling
},
=> The dropdown is never opened while scrolling. It is also automatically closed as soon as you start scrolling in the main-column. However scrolling inside the dropdown is possible. Of course you can also cahnge it to something like
expanded = if (isScrolling) expanded else !expanded
To just leave everything like it is while scrolling