Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RegisterScreen:transition was used multiple times #410

Open
ArleyPereira opened this issue May 6, 2024 · 14 comments
Open

RegisterScreen:transition was used multiple times #410

ArleyPereira opened this issue May 6, 2024 · 14 comments

Comments

@ArleyPereira
Copy link

There is an issue I run into when I quickly double click a button that navigates to another screen in my Compose.

it looks like androidx.lifecycle:lifecycle added dropUnlessResumed in version 2.8.0-alpha02.

https://developer.android.com/jetpack/androidx/releases/lifecycle?hl=pt-br

I tested with version 2.8.0-rc01, but I got the error: CompositionLocal LocalLifecycleOwner not present.

voyager = "1.1.0-alpha04"
composeBom = "2024.05.00"
kotlin = "1.9.23"

FATAL EXCEPTION: main (Ask Gemini)
Process: br.com.hellodev.find360, PID: 15156
java.lang.IllegalArgumentException: Key br.com.hellodev.signup.presenter.screens.register.screen.RegisterScreen:transition was used multiple times
	at androidx.compose.runtime.saveable.SaveableStateHolderImpl$SaveableStateProvider$1$1.invoke(SaveableStateHolder.kt:89)
	at androidx.compose.runtime.saveable.SaveableStateHolderImpl$SaveableStateProvider$1$1.invoke(SaveableStateHolder.kt:88)
	at androidx.compose.runtime.DisposableEffectImpl.onRemembered(Effects.kt:83)
	at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:1295)
	at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:984)
	at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:1005)
	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:639)
	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:551)
	at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:41)
	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:1648)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1659)
	at android.view.Choreographer.doCallbacks(Choreographer.java:1129)
	at android.view.Choreographer.doFrame(Choreographer.java:1045)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1622)
	at android.os.Handler.handleCallback(Handler.java:958)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loopOnce(Looper.java:230)
	at android.os.Looper.loop(Looper.java:319)
	at android.app.ActivityThread.main(ActivityThread.java:8893)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:608)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@a7d1990, androidx.compose.ui.platform.MotionDurationScaleImpl@a6df289, StandaloneCoroutine{Cancelling}@fac328e, AndroidUiDispatcher@b8cfaaf]
@Moozart
Copy link

Moozart commented May 7, 2024

I think problem occurs when multiple click to navigate or same screen popud. If u remove SlideAnimation etc.. error can be gone. But I applied this solution for my project. I hope solver your problem.

override val key: ScreenKey = super.key + "${Random.nextDouble(Double.MIN_VALUE, Double.MAX_VALUE)}"

@ArleyPereira
Copy link
Author

Thanks for your tip. I will try to apply it, while there is no solution from the library. From what I've seen, this error has been known for some time, but so far it still persists.

@wiryadev
Copy link
Contributor

wiryadev commented May 11, 2024

Face the same error. I used the 1.0.0 tho.

Edit: i tried 1.1.0 alpha04 and still the transition error

@qdsfdhvh
Copy link
Contributor

I face the same crash,but i am upgrading to kotlin 2.0.0-RC2 and use release apk (r8), when I had open a object screen, and then open another object screen, app will be crash with it, i'm not sure what's the reason for that at the moment.
ps: seem use data object can solve my problem.

@wiryadev
Copy link
Contributor

Hi, i just found out something:

  1. dropUnlessResumed not working for this, not because of lifecycle owner, but when you click that quickly, the state is still resumed (check screenshot below)
  2. We can pass our own LifecycleOwner, but make sure you use LocalLifecyleOwner from androidx.compose.ui.platform, not the one from androidx.lifecycle.compose.

image

@itsallan
Copy link

I think problem occurs when multiple click to navigate or same screen popud. If u remove SlideAnimation etc.. error can be gone. But I applied this solution for my project. I hope solver your problem.

override val key: ScreenKey = super.key + "${Random.nextDouble(Double.MIN_VALUE, Double.MAX_VALUE)}"

thanks mate this worked for me

@DevSrSouza
Copy link
Collaborator

Does this also happen when using override val key = uniqueScreenKey ?
https://voyager.adriel.cafe/state-restoration#identifying-screens

@ArleyPereira
Copy link
Author

The error does not occur when I use 'override val key = uniqueScreenKey', but there is duplication of navigation. I will test 'dropUnlessResumed' for click

@adrict99
Copy link

adrict99 commented May 23, 2024

Same is happening to me, if I remove SlideTransition I lose "back" functionality using if (navigator.canPop) navigator.pop(), and if I have SlideTransition, when I do navigator.pop() it shows the same screen from where I'm navigating after animating like if it was going to a different one.

But in this project: https://github.com/YanneckReiss/compose-multiplatform-navigation-voyager-showcase it is working somehow

@wiryadev
Copy link
Contributor

I will test 'dropUnlessResumed' for click

Last time i tried, it doesnt work. So i use debounced click instead from this.

@elsheikhayman
Copy link

I faced this problem too when a navigation button was clicked twice quickly, causing the app to crash during the transition. My solution was to check if the destination screen is the same as the current one and avoid calling navigator.push. Here's the extension on the Navigator I made:

private inline fun <reified T : Screen> Navigator.navigate(screen: T) { if (lastItem !is T) { push(screen) } }

usage:
navigator.navigate(OnBoardingStep2Screen())

Hope this helps!

@adrict99
Copy link

@elsheikhayman Thank you for your comment! I tried it, but unfortunately it didn't work for me. I debugged the stack items and the last item and apparently it is fine and no screen is present twice, also the last screen in the stack is always the one I'm in.

The navigation is ScreenA -> ScreenB -> ScreenC, and for some reason I can't do pop or popUntil to navigate back, but I can do popAll or popUntilRoot.

When I click on a something that uses pop or popUntil in ScreenC, the stack gets printed like: stack -> ScreenA,ScreenB, lastItem -> ScreenB, just after that it automatically prints: stack -> ScreenA,ScreenB,ScreenC, last item -> ScreenC, like if it was navigating again to the same screen where it is.

@adrict99
Copy link

adrict99 commented May 30, 2024

I got it working after long debugging, I realized I was holding the screen state and actions in the screen model with a sealed interface, creating a mutableStateFlow, and subscribing to it via observeAsState in the Screen to respond appropriately, if I remove this the navigation works perfectly, I don't get the "Transition was used multiple times" (with and without SlideTransition) and even the pop function works.

It is the only solution I got working, but I don't like handling states in the UI, is this going to be fixed anytime soon or am I doing something wrong? Thanks

Edit:
If I use the state as described in https://voyager.adriel.cafe/screenmodel/coroutines-integration/, and I don't navigate based on the subscribed state it also works, but I think this is not how it should work.

Edit 2:
If you put a log on every screen to debug the navigation Stack you will see that it gets printed multiple times, if you navigate based on a subscribed state, it's not going to work fine since you will be doing it multiple times or at least that's my theory at the moment. No subscribed navigation, no problem.

@ArleyPereira
Copy link
Author

You can browse based on state and every time you browse, you clear the state immediately.

LaunchedEffect(state.navigateToScreen) {
    state.navigateToScreen?.let {
        navigationToConfirmRegister()
        action(SignupAction.ClearNavigate)
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants