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 ViewModel
s and ScreenLifecycleProvider
. This seems to be a bug introduced in 1.0.0-rc06
.
Some context: I have some Tab
s, 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 doingoverride fun getLifecycleOwner(): ScreenLifecycleOwner = AndroidScreenLifecycleOwner.get(this)
? Why not? ScreenLifecycleProvider.get(screen)
crashes in1.0.0-rc06
. In1.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)
@Syer10 https://github.com/adrielcafe/voyager/pull/212 from @programadorthi also seems to fix this issue since
AndroidScreenLifecycleOwner
has theseprovides
:Just tested it out and it seems to be working well.
Fixed on
1.0.0-rc09