[BUG] ModalBottomSheet NavHost animateContentSize issues
tomczyn opened this issue · comments
There appears to be some animation issues when using animateContentSize
on a NavHost
Modifier
when the content is inside the material3 ModalBottomSheet
. Here's the demo of how the same code behaves with the NavHost and without the NavHost.
Recording:
https://drive.proton.me/urls/P8STB5F53G#mZ9le5bjPJB3
Issues with comparison to code without NavHost
:
- Showing bottom sheet animation hangs for ~0,6 seconds at the bottom of the screen. By default,
ModalBottomSheet
finishes showing animation in around ~400ms, but withNavHost
content andanimateContentSize
modifier it hangs at the bottom barely visible for ~600ms and then performs ~400ms showing animation. - Sheet doesn't animate alongside the Android keyboard (IME padding) but does it one after the other. E.g. when navigating inside the sheet to a route that has different size while hiding the keyboard, it first animates the content size of the new route and then hides the keyboard as two separate animations. For example, when not using
NavHost
compose animates both events at the same time (see example video).
Example code
@Composable
fun App() {
Column(modifier = Modifier.fillMaxSize()) {
var showBottomSheet by remember { mutableStateOf(false) }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
if (showBottomSheet) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = { showBottomSheet = false },
content = {
val navigator = rememberNavigator()
NavHost(
modifier = Modifier.animateContentSize(),
navigator = navigator,
navTransition = NavTransition(),
initialRoute = "/sheet1",
) {
scene(route = "/sheet1") {
Content(
modifier = Modifier.fillMaxWidth().height(200.dp),
title = "NavHost 1",
onClick = { navigator.navigate("/sheet2") }
)
}
scene(route = "/sheet2") {
Content(
modifier = Modifier.fillMaxWidth().height(350.dp),
title = "NavHost 2",
onClick = { showBottomSheet = false }
)
}
}
}
)
}
}
}
@Composable
private fun Content(
modifier: Modifier,
title: String,
onClick: () -> Unit,
) {
Column(
modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
var field by remember { mutableStateOf("") }
Text(title)
TextField(
value = field,
onValueChange = { field = it })
Button(onClick = { onClick() }) {
Text("Next")
}
}
}
There are no issues/interference with the ModalBottomSheet
animations without the animateContentSize
Modifier
on the NavHost
, but unfortunately ModalBottomSheet
doesn't animate its content height by default so without this modifier when navigating to routes with different heights the content simply jumps instead of showing nice animation.
Here's the demo code from the recording: https://github.com/tomczyn/PreCompose-Sample/tree/animations
The issue might be related to AnimatedContent
, as PreCompose uses it internally to manage navigation transitions. A quick workaround could be to configure the navTransition
so that it does not trigger any animations.
I poked around a little bit more in the code, and it appears that the animateContentSize
interferes with the ModalBottomSheet
animations when it has nothing to compose inside the internal BoxWithConstraints
of the NavHost
composable. Not composing the layout at all when there's nothing to show seems to resolve the issue. Do you think that'd be a valid fix? I've opened the PR for it #315
Here's the above sample with a fix, you can test it by running sample.todo.android
: https://github.com/tomczyn/PreCompose/tree/animations_fix
The above fix solves the bottom sheet showing animation issue. But the keyboard issue still remains. It appears that this one is connected to the pauseTransition
from AnimatedContent
as you pointed out. After defining this particular transitions as None
, the keyboard starts to animate alongside the content changes as expected.
The issue with the keyboard is actually because when the content goes off the screen, the field that had a focus is still being composed for the animation. That's why defining no animation solves it, and with animation the keyboard is waiting until the end to be hidden. Adding manual clear focus through LocalFocusManager
solves the issue. So the only real problem was animateContentSize
which is now fixed.