voyager: `AndroidScreen` is not working as intended in `1.0.0-rc06` - Hilt

Hello,

Recently I’ve started using Voyager. I’ve been enjoying how simple it is and how quick we can start getting into it, but have also been getting a few issues. I might have missed something, but the documentation is confusing regarding AndroidScreen, Hilt ViewModels and ScreenLifecycleProvider. This seems to be a bug introduced in 1.0.0-rc06.

Some context: I have some Tabs, and each Tab has its own Navigator with a SlideTransition. In each Tab, I can push an Item (which is a Screen)

First, I tried using AndroidScreen with unique keys for my Item. When I navigate from any Tab to this AndroidScreen, everything works, and my ViewModel is correctly created.

The issue is when navigating back to the initial screen of the Tab, and navigating to this AndroidScreen again, but this time for another item (which loads different data but is the same screen type). When loading the new item, it will show all the old data for the item that was first shown - as in, it’s the same ViewModel as the first Item.

Apparently AndroidScreen uses DefaultScreenLifecycleOwner, and when using getViewModel() it will always use the parent Activity as the owner, which means it will always be the same ViewModel for all these screens. This was introduced in 1.0.0-rc06(https://github.com/adrielcafe/voyager/commit/33e28d258ea54906623b845d796c0e0e861814ef)

I believe this goes against what is specified in the documentation.

According to the Documentation, we can also use ScreenLifecycleProvider to get a similar behaviour as AndroidScreen, but using ScreenLifecycleProvider.get(...) actually gives us the expected behaviour since it will create a ScreenLifecycleOwner, which should be unique for each screen (provided that the key is unique, I suppose).

But when I tried to use ScreenLifecycleProvider.get(screen) by overriding ScreenLifecycleProvider, I was getting this error:

Crash: java.lang.IllegalArgumentException: Key Screen#0131d128-773c-4d9d-b1e1-4ba91ed961a5:transition:lifecycle 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:81) at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:1105) at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:820) at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:842) at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:592) at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:510) 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:970) at android.view.Choreographer.doCallbacks(Choreographer.java:796) at android.view.Choreographer.doFrame(Choreographer.java:727) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7656) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@22011f3, androidx.compose.ui.platform.MotionDurationScaleImpl@211deb0, StandaloneCoroutine{Cancelling}@f95a129, AndroidUiDispatcher@877adf1]

Downgrading Voyager to 1.0.0-rc05 solved the issue though, so it seems like a bug introduced with 1.0.0-rc06? Everything works as expected using ScreenLifecycleProvider.get(screen) or when using AndroidScreen in 1.0.0-rc05, as it also uses ScreenLifecycleProvider.get(screen).

So, I guess there are two points here:

  • Shouldn’t AndroidScreen still be doing override fun getLifecycleOwner(): ScreenLifecycleOwner = AndroidScreenLifecycleOwner.get(this)? Why not?
  • ScreenLifecycleProvider.get(screen) crashes in 1.0.0-rc06. In 1.0.0-rc05 it works as expected.

The documentation also says it gets a key but it actually needs the whole Screen (this). I guess this is just slightly outdated.

I might be missing some details here though, so feel free to point out any mistakes or things I might’ve misunderstood. Let me know if more context is needed, I can’t share the codebase but can give more details into what we’re doing.

Thank you in advance for your help!

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 8
  • Comments: 36 (3 by maintainers)

Most upvoted comments

@Syer10 https://github.com/adrielcafe/voyager/pull/212 from @programadorthi also seems to fix this issue since AndroidScreenLifecycleOwner has these provides:

LocalLifecycleOwner provides this,
LocalViewModelStoreOwner provides this,

Just tested it out and it seems to be working well.

Fixed on 1.0.0-rc09