Tlaster / PreCompose

Compose Multiplatform Navigation && State Management

Home Page:https://tlaster.github.io/PreCompose/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[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 with NavHost content and animateContentSize 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.