0

When using the Accompanist AnimatedNavHost and navigation animations, if I go to a screen and then back (using system back button) quickly a few times in a row, the app crashes. I suspect this is a bug in Accompanist (I've submitted an issue on the Github repo), but I figured it was worth posting here as well, in case anybody else has seen this and/or has a workaround.

The app crashes with the following stacktrace:

java.util.NoSuchElementException: List contains no element matching the predicate.
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$finalEnter$1$1.invoke(AnimatedNavHost.kt:341)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$finalEnter$1$1.invoke(AnimatedNavHost.kt:173)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$7$1.invoke(AnimatedNavHost.kt:244)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$7$1.invoke(AnimatedNavHost.kt:244)
        at androidx.compose.animation.AnimatedContentKt.AnimatedContent(AnimatedContent.kt:664)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt.AnimatedNavHost(AnimatedNavHost.kt:242)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$10.invoke(Unknown Source:23)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$10.invoke(Unknown Source:10)
        at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:140)
        at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2158)
        at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2404)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2585)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2571)
        at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:571)
        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2571)
        at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2547)
        at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:620)
        at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:786)
        at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:105)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:456)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:425)
        at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
        at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
        at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
        at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:965)
        at android.view.Choreographer.doCallbacks(Choreographer.java:791)
        at android.view.Choreographer.doFrame(Choreographer.java:722)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

My navigation code is as follows:

AnimatedNavHost(navController = navController, startDestination = startDestinationString) {
                composable(
                    route = "home",
                ) {
                    HomeScreen(
                        homeViewModel = homeViewModel,
                        navigateToBookDetail = { book ->
                            val arguments =
                                Bundle().apply {
                                    putParcelable("book", book)
                                }
                            navController.navigate(route = "book_detail", args = arguments)
                        }
                    )
                }
                composable(
                    route = "book_detail",
                    enterTransition = { _, _ ->
                        slideInHorizontally(initialOffsetX = { 1500 }, animationSpec = tween(250))
                    },
                    popExitTransition = { _, _ ->
                        slideOutHorizontally(targetOffsetX = { 1500 }, animationSpec = tween(250))
                    }
                ) { backstackEntry ->
                    val book = backstackEntry.arguments?.getParcelable<Book>("book")
                    if (book != null) {
                        BookDetailScreen(viewModel = bookDetailViewModel, book = book)
                    }
                }
            }

Screen recording

To Reproduce

Navigate quickly between two screens, using the system back button to return to the first screen before the animation completes. In my case I'm triggering the animation and using the system back button before the animation completes. This usually takes a couple of tries before it crashes. Sometimes as little as two, sometimes five or ten.

Version 0.21.0-beta of Accompanist Animated Navigation

Cameron
  • 1,281
  • 1
  • 19
  • 40

1 Answers1

0

Update:

Looks like it got fixed with 0.21.1-beta and I just saw that you opened the issue for this on github.

I had the same problem where switching between the tabs to fast triggered the same Exception, but thankfully I found the code from Philip Dukhov. He had the same problem and the solution was to start a coroutine and cancel it when navigating before the animation finishes.

abdo1504
  • 26
  • 1
  • 1
  • 5
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 06 '21 at 22:25