adrielcafe / voyager Goto Github PK
View Code? Open in Web Editor NEW🛸 A pragmatic navigation library for Jetpack Compose
Home Page: https://voyager.adriel.cafe
License: MIT License
🛸 A pragmatic navigation library for Jetpack Compose
Home Page: https://voyager.adriel.cafe
License: MIT License
Currently the BackHandler is handled as
BackHandler(
enabled = sheetState.isVisible,
This causes problems about the order of the backhandlers when others views have registered a BackHandler after the bottomsheet navigator as they will get the event first and will act on it before the BottomSheet.
The proper way is to add the back handler only when the sheet is visible, meaning it's always added last and will have the priority.
So
if (sheetState.isVisible) {
BackHandler(
Hello, I have faced with issue.
Steps to reproduce:
Version: 1.0.0-rc01
Sample reproducable project: https://github.com/egorikftp/VoyagerCrash
Crash log:
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.egoriku.voyagercrash.HomeTab)
at android.os.Parcel.writeSerializable(Parcel.java:2165)
at android.os.Parcel.writeValue(Parcel.java:1931)
at android.os.Parcel.writeList(Parcel.java:1140)
at android.os.Parcel.writeValue(Parcel.java:1880)
at android.os.Parcel.writeList(Parcel.java:1140)
at android.os.Parcel.writeValue(Parcel.java:1880)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1023)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1092)
at android.os.Parcel.writeValue(Parcel.java:1849)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1023)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1092)
at android.os.Parcel.writeValue(Parcel.java:1849)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1690)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1727)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:150)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7870)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.io.NotSerializableException: androidx.compose.material.DrawerState
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
at android.os.Parcel.writeSerializable(Parcel.java:2160)
at android.os.Parcel.writeValue(Parcel.java:1931)
at android.os.Parcel.writeList(Parcel.java:1140)
at android.os.Parcel.writeValue(Parcel.java:1880)
at android.os.Parcel.writeList(Parcel.java:1140)
at android.os.Parcel.writeValue(Parcel.java:1880)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1023)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1092)
at android.os.Parcel.writeValue(Parcel.java:1849)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1023)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1092)
at android.os.Parcel.writeValue(Parcel.java:1849)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1690)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1727)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:150)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7870)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Last version introduced a memory leak:
┬───
│ GC Root: Input or output parameters in native code
│
├─ dalvik.system.PathClassLoader instance
│ Leaking: NO (ScreenLifecycleStore↓ is not leaking and A ClassLoader is never leaking)
│ ↓ ClassLoader.runtimeInternalObjects
├─ java.lang.Object[] array
│ Leaking: NO (ScreenLifecycleStore↓ is not leaking)
│ ↓ Object[2112]
├─ cafe.adriel.voyager.core.lifecycle.ScreenLifecycleStore class
│ Leaking: NO (a class is never leaking)
│ ↓ static ScreenLifecycleStore.owners
│ ~~~~~~
├─ java.util.concurrent.ConcurrentHashMap instance
│ Leaking: UNKNOWN
│ Retaining 10.7 MB in 81204 objects
│ ↓ ConcurrentHashMap["Screen#11"]
│ ~~~~~~~~~~~~~
├─ cafe.adriel.voyager.androidx.AndroidScreenLifecycleOwner instance
│ Leaking: UNKNOWN
│ Retaining 775 B in 28 objects
│ ↓ AndroidScreenLifecycleOwner.atomicContext
│ ~~~~~~~~~~~~~
├─ java.util.concurrent.atomic.AtomicReference instance
│ Leaking: UNKNOWN
│ Retaining 12 B in 1 objects
│ value instance of app.symfonik.ui.MainActivity with mDestroyed = true
│ ↓ AtomicReference.value
│ ~~~~~
╰→ app.symfonik.ui.MainActivity instance
Leaking: YES (ObjectWatcher was watching this because app.symfonik.ui.MainActivity received Activity#onDestroy()
callback and Activity#mDestroyed is true)
Retaining 10.7 MB in 81168 objects
key = 2b6ebb8b-dc34-4653-bce5-8a807c33b146
watchDurationMillis = 6356
retainedDurationMillis = 1355
mApplication instance of app.symfonik.Application
mBase instance of android.app.ContextImpl
I'm attempting to migrate my app over to use Voyager, and I'm a big fan so far. One thing I'm struggling with is transitions. While using a SlideTransition, some of my screens "flicker" (get recomposed multiple times) and causes visual jitter. I understand that using a transition may cause a Composable to re-compose multiple times, but it seems like it shouldn't be visible to the user the way I'm seeing it.
Here is the basic code for what I'm doing without the transition:
setContent {
LunaTheme {
BottomSheetNavigator {
Navigator(screen = HomeScreen())
}
}
}
and with transition:
setContent {
LunaTheme {
BottomSheetNavigator {
Navigator(screen = HomeScreen()) { navigator ->
SlideTransition(navigator = navigator)
}
}
}
}
and here are the results I'm getting. I have a function on each screen that logs when a composable is re-composed as well as the number of times that function has been recomposed. A number 0 indicates that the composable was remade from scratch, as opposed to just being re-composed because of a variable change.
Without transition:
In the video you can see that things navigate properly (the detail screen is just a loading spinner), and when navigating back to the home screen using the system back button, everything looks fine. In the logs you can see the HomeScreen is only recomposed once when returning from the BookDetailScreen.
With transition:
You can see in this video that the loading spinner on the detail page gets completely restarted once, and when returning to the home screen, that screen also gets completely reloaded once, creating this visual jitter. You can also see in the logs (which also include the stack each time a navigation is performed) that when navigating to the detail page, it is re-composed from scratch with a value of zero in the logs four times, and the same happens when returning to the HomeScreen, it's restarted four times.
I'm not sure if I'm misusing the Transition here, or if this is a bug in the library, so any help would be much appreciated!
Hello, I am trying ot use cafe.adriel.voyager.kodein.rememberScreenModel
to create my screen model but it is not available in commonMain
.
Hi Mr Adriel,
Each time I add the dependencies to my project, I get the error below.
My compose version is 1.0.0-beta08 so I think it might be a versioning issue.
java.lang.NoSuchFieldError: No field None of type Landroidx/compose/ui/text/input/KeyboardCapitalization; in class Landroidx/compose/ui/text/input/KeyboardCapitalization; or its superclasses (declaration of 'androidx.compose.ui.text.input.KeyboardCapitalization' appears in /data/app/~~fNIyLfrseWUXElCjs4UbBQ==/com.example.sizingapplication-p97FFtKhLr3pGex5Onsc9w==/base.apk)
at androidx.compose.foundation.text.KeyboardOptions.(KeyboardOptions.kt:47)
at androidx.compose.foundation.text.KeyboardOptions.(KeyboardOptions.kt:56)
at com.example.sizingapplication.MainActivityKt$InputLayout$1.invoke(MainActivity.kt:132)
at com.example.sizingapplication.MainActivityKt$InputLayout$1.invoke(MainActivity.kt:117)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.material.ScaffoldKt$ScaffoldLayout$1$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:316)
at androidx.compose.material.ScaffoldKt$ScaffoldLayout$1$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:314)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2$1$1.invoke(SubcomposeLayout.kt:241)
at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2$1$1.invoke(SubcomposeLayout.kt:241)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.ComposerKt.invokeComposable(Composer.kt:3330)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2577)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2573)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:540)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2566)
at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:2517)
at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:476)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:727)
at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:2980)
at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:432)
at androidx.compose.ui.layout.SubcomposeLayoutState.subcomposeInto(SubcomposeLayout.kt:259)
at androidx.compose.ui.layout.SubcomposeLayoutState.access$subcomposeInto(SubcomposeLayout.kt:145)
at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2.invoke(SubcomposeLayout.kt:234)
at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2.invoke(SubcomposeLayout.kt:231)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.withNoObservations(SnapshotStateObserver.kt:144)
at androidx.compose.ui.node.OwnerSnapshotObserver.withNoSnapshotReadObservation$ui_release(OwnerSnapshotObserver.kt:49)
at androidx.compose.ui.node.LayoutNode.withNoSnapshotReadObservation$ui_release(LayoutNode.kt:1107)
at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose(SubcomposeLayout.kt:231)
at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose(SubcomposeLayout.kt:226)
at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose$ui_release(SubcomposeLayout.kt:215)
at androidx.compose.ui.layout.SubcomposeLayoutState$Scope.subcompose(SubcomposeLayout.kt:466)
at androidx.compose.material.ScaffoldKt$ScaffoldLayout$1$1$1.invoke(Scaffold.kt:314)
at androidx.compose.material.ScaffoldKt$ScaffoldLayout$1$1$1.invoke(Scaffold.kt:241)
at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
2021-07-24 17:47:41.724 7238-7238/com.example.sizingapplication E/AndroidRuntime: at androidx.compose.ui.layout.SubcomposeLayoutState$createMeasurePolicy$1$measure$1.placeChildren(SubcomposeLayout.kt:357)
at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:925)
at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:915)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:75)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutSnapshotReads$ui_release(OwnerSnapshotObserver.kt:56)
at androidx.compose.ui.node.LayoutNode.layoutChildren$ui_release(LayoutNode.kt:915)
at androidx.compose.ui.node.LayoutNode.onNodePlaced$ui_release(LayoutNode.kt:901)
at androidx.compose.ui.node.InnerPlaceable.placeAt-f8xVGno(InnerPlaceable.kt:94)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.node.OuterMeasurablePlaceable.placeAt-f8xVGno(OuterMeasurablePlaceable.kt:149)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default(Placeable.kt:203)
at androidx.compose.foundation.layout.BoxKt.placeInBox(Box.kt:186)
at androidx.compose.foundation.layout.BoxKt.access$placeInBox(Box.kt:1)
at androidx.compose.foundation.layout.BoxKt$boxMeasurePolicy$1$measure$2.invoke(Box.kt:126)
at androidx.compose.foundation.layout.BoxKt$boxMeasurePolicy$1$measure$2.invoke(Box.kt:125)
at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:925)
at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:915)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:75)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutSnapshotReads$ui_release(OwnerSnapshotObserver.kt:56)
at androidx.compose.ui.node.LayoutNode.layoutChildren$ui_release(LayoutNode.kt:915)
at androidx.compose.ui.node.LayoutNode.onNodePlaced$ui_release(LayoutNode.kt:901)
at androidx.compose.ui.node.InnerPlaceable.placeAt-f8xVGno(InnerPlaceable.kt:94)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default(Placeable.kt:203)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper$measure$1$1.placeChildren(DelegatingLayoutNodeWrapper.kt:123)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:111)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default(Placeable.kt:203)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper$measure$1$1.placeChildren(DelegatingLayoutNodeWrapper.kt:123)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:111)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeWithLayer(Placeable.kt:393)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeWithLayer$default(Placeable.kt:266)
at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier$measure$1.invoke(GraphicsLayerModifier.kt:221)
2021-07-24 17:47:41.730 7238-7238/com.example.sizingapplication E/AndroidRuntime: at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier$measure$1.invoke(GraphicsLayerModifier.kt:220)
at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:111)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default(Placeable.kt:203)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper$measure$1$1.placeChildren(DelegatingLayoutNodeWrapper.kt:123)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:111)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative(Placeable.kt:359)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative$default(Placeable.kt:179)
at androidx.compose.foundation.layout.FillModifier$measure$1.invoke(Size.kt:632)
at androidx.compose.foundation.layout.FillModifier$measure$1.invoke(Size.kt:631)
at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:111)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeWithLayer-aW-9-wM(Placeable.kt:396)
at androidx.compose.ui.node.OuterMeasurablePlaceable.placeAt-f8xVGno(OuterMeasurablePlaceable.kt:151)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelativeWithLayer(Placeable.kt:385)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelativeWithLayer$default(Placeable.kt:246)
at androidx.compose.ui.layout.RootMeasurePolicy$measure$2.invoke(RootMeasurePolicy.kt:43)
at androidx.compose.ui.layout.RootMeasurePolicy$measure$2.invoke(RootMeasurePolicy.kt:42)
at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:925)
at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:915)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:1776)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:123)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:75)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutSnapshotReads$ui_release(OwnerSnapshotObserver.kt:56)
at androidx.compose.ui.node.LayoutNode.layoutChildren$ui_release(LayoutNode.kt:915)
at androidx.compose.ui.node.LayoutNode.onNodePlaced$ui_release(LayoutNode.kt:901)
at androidx.compose.ui.node.InnerPlaceable.placeAt-f8xVGno(InnerPlaceable.kt:94)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default(Placeable.kt:203)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper$measure$1$1.placeChildren(DelegatingLayoutNodeWrapper.kt:123)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:111)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default(Placeable.kt:203)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper$measure$1$1.placeChildren(DelegatingLayoutNodeWrapper.kt:123)
2021-07-24 17:47:41.733 7238-7238/com.example.sizingapplication E/AndroidRuntime: at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:111)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50$default(Placeable.kt:203)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper$measure$1$1.placeChildren(DelegatingLayoutNodeWrapper.kt:123)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:111)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
at androidx.compose.ui.node.OuterMeasurablePlaceable.placeAt-f8xVGno(OuterMeasurablePlaceable.kt:149)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative(Placeable.kt:359)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative$default(Placeable.kt:179)
at androidx.compose.ui.node.LayoutNode.place$ui_release(LayoutNode.kt:803)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:213)
at androidx.compose.ui.platform.AndroidComposeView.onMeasure(AndroidComposeView.android.kt:547)
at android.view.View.measure(View.java:25466)
at androidx.compose.ui.platform.AbstractComposeView.internalOnMeasure$ui_release(ComposeView.android.kt:278)
at androidx.compose.ui.platform.AbstractComposeView.onMeasure(ComposeView.android.kt:265)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:25466)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:747)
at android.view.View.measure(View.java:25466)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3397)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2228)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2486)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:731)
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)
Declaring a TabNavigator
before a BottomSheetNavigator
leads to the crash below in version 1.0.0-beta10
but works just fine in 1.0.0-beta09
.
TabNavigator(HomeTab) {
BottomSheetNavigator {
CurrentTab()
}
}
java.lang.IllegalArgumentException: Key cafe.adriel.voyager.navigator.bottomSheet.HiddenBottomSheetScreen 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:781)
at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:639)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:733)
at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:2991)
at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:433)
at androidx.compose.ui.layout.SubcomposeLayoutState.subcomposeInto(SubcomposeLayout.kt:262)
at androidx.compose.ui.layout.SubcomposeLayoutState.access$subcomposeInto(SubcomposeLayout.kt:148)
at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2.invoke(SubcomposeLayout.kt:237)
at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2.invoke(SubcomposeLayout.kt:234)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.withNoObservations(SnapshotStateObserver.kt:142)
at androidx.compose.ui.node.OwnerSnapshotObserver.withNoSnapshotReadObservation$ui_release(OwnerSnapshotObserver.kt:55)
at androidx.compose.ui.node.LayoutNode.withNoSnapshotReadObservation$ui_release(LayoutNode.kt:1122)
at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose(SubcomposeLayout.kt:234)
at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose(SubcomposeLayout.kt:229)
at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose$ui_release(SubcomposeLayout.kt:218)
at androidx.compose.ui.layout.SubcomposeLayoutState$Scope.subcompose(SubcomposeLayout.kt:469)
at androidx.compose.foundation.layout.BoxWithConstraintsKt$BoxWithConstraints$1$1.invoke-0kLqBqw(BoxWithConstraints.kt:66)
at androidx.compose.foundation.layout.BoxWithConstraintsKt$BoxWithConstraints$1$1.invoke(BoxWithConstraints.kt:64)
at androidx.compose.ui.layout.SubcomposeLayoutState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:348)
at androidx.compose.ui.node.InnerPlaceable.measure-BRTryo0(InnerPlaceable.kt:50)
at androidx.compose.ui.node.OuterMeasurablePlaceable$remeasure$3.invoke(OuterMeasurablePlaceable.kt:100)
at androidx.compose.ui.node.OuterMeasurablePlaceable$remeasure$3.invoke(OuterMeasurablePlaceable.kt:99)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:126)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:88)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:76)
at androidx.compose.ui.node.OuterMeasurablePlaceable.remeasure-BRTryo0(OuterMeasurablePlaceable.kt:99)
at androidx.compose.ui.node.OuterMeasurablePlaceable.measure-BRTryo0(OuterMeasurablePlaceable.kt:71)
at androidx.compose.ui.node.LayoutNode.measure-BRTryo0(LayoutNode.kt:1242)
at androidx.compose.ui.layout.RootMeasurePolicy.measure-3p2s80s(RootMeasurePolicy.kt:38)
at androidx.compose.ui.node.InnerPlaceable.measure-BRTryo0(InnerPlaceable.kt:50)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.measure-BRTryo0(DelegatingLayoutNodeWrapper.kt:118)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.measure-BRTryo0(DelegatingLayoutNodeWrapper.kt:118)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.measure-BRTryo0(DelegatingLayoutNodeWrapper.kt:118)
at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.measure-BRTryo0(DelegatingLayoutNodeWrapper.kt:118)
An easy way to fix this is simply by declaring the BottomSheetNavigator first, but that doesn't explain why this code is suddenly not working anymore.
BottomSheetNavigator {
TabNavigator(PersonalTab) {
CurrentTab()
}
}
Hello, my application crashes when changing screens with animated content.
I use code from examples. I think is a problem for all users.
kotlin_version '1.5.31'
voyager_version = "1.0.0-beta13"
compose_version = '1.1.0-alpha06'
Repository for reproduce - https://github.com/jershell/voyager-bug-reproduce/tree/main
Flow
java.lang.IllegalArgumentException: Key com.github.navtest.ScreenFoo was used multiple times
Stacktrace
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.github.navtest, PID: 2788
java.lang.IllegalArgumentException: Key com.github.navtest.ScreenFoo 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:802)
at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:647)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:488)
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:964)
at android.view.Choreographer.doCallbacks(Choreographer.java:790)
at android.view.Choreographer.doFrame(Choreographer.java:721)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
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)
When clicking outside the bottomsheet to close it, the navigator is not involved currently, meaning that it won't replace the stack leading to display issue on the next show + keeping the composables / viewmodel in memory.
Solution is to use the confirmStateChange parameter of the sheet state and when the target is Hidden, manually call the navigator hide and return false to not let the default handling happen.
Using stateIn
operator inside ScreenModel
using built-in coroutineScope cause a crash with java.lang.IllegalStateException: ScreenModel not found
. However, this exception doesn't happen if self-created Coroutine scope is used.
Example code,
class TestScreen : Screen {
@Composable
override fun Content() {
val sm = rememberScreenModel { TestScreenModel() }
val msg by sm.msg.collectAsState()
Text(msg)
}
}
class TestScreenModel : ScreenModel {
val msg = msgFlow.stateIn(coroutineScope, SharingStarted.Lazily, "")
}
val msgFlow = listOf("Hello", "world!").asFlow()
Minimizing App causing crash. Here is the error report:
Process: media.uqab.planit, PID: 6479
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = media.uqab.planit.task.ui.screen.AllTaskScreen)
at android.os.Parcel.writeSerializable(Parcel.java:1833)
at android.os.Parcel.writeValue(Parcel.java:1780)
at android.os.Parcel.writeList(Parcel.java:1045)
at android.os.Parcel.writeValue(Parcel.java:1729)
at android.os.Parcel.writeList(Parcel.java:1045)
at android.os.Parcel.writeValue(Parcel.java:1729)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at android.os.Parcel.writeValue(Parcel.java:1698)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at android.os.Parcel.writeValue(Parcel.java:1698)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4505)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145)
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)
Caused by: java.io.NotSerializableException: media.uqab.planit.task.ui.activity.TaskActivity
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
at android.os.Parcel.writeSerializable(Parcel.java:1828)
at android.os.Parcel.writeValue(Parcel.java:1780)
at android.os.Parcel.writeList(Parcel.java:1045)
at android.os.Parcel.writeValue(Parcel.java:1729)
at android.os.Parcel.writeList(Parcel.java:1045)
at android.os.Parcel.writeValue(Parcel.java:1729)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at android.os.Parcel.writeValue(Parcel.java:1698)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at android.os.Parcel.writeValue(Parcel.java:1698)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4505)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145)
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 Implementation:
Activity:
@AndroidEntryPoint
class TaskActivity: AppCompatActivity() {
@OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
window.statusBarColor = Color.White.toArgb()
setContent {
Navigator(
screen = AllTaskScreen(
onBackPress = { finish() }
)
) { navigation ->
ScaleTransition(navigator = navigation)
}
}
}
}
AllTaskScreen:
class AllTaskScreen(
private val onBackPress: () -> Unit = {}
): AndroidScreen() {
@Composable
override fun Content() {
val viewModel = getViewModel<TaskViewModel>()
val navigator = LocalNavigator.currentOrThrow
// ... rest of the compose functions
}
}
Hi, many thanks for your library. I have tried it a bit and it's very powerful.
I have noticed when I wrap Navigator with AnimatedVisibility it is reset to the initial screen.
I created my own scaffold with slots, I need to use different navigators in each slot. In one slot in the scaffold root, I need to use AnimatedVisibility to produce hide/collapse animation, and in this slot I need to maintain some navigation. So after I create navigation in that AnimatedVisibility composable I have noticed that when it exit to the invisible state navigator also resets its state to the initial state.
Can you provide some ideas of how I can achieve some animation around box that contains navigator ?
fun login(email: String, password: String) = coroutineScope.launch {
val userCredentials = UserCredentials(email, password)
logInUseCase.invoke(userCredentials)
}
@Test
fun `on login success state is SignIn`() = runTest {
loginModel.login("email", "password")
loginModel.state
.take(1)
.onEach { assertEquals(it::class.simpleName, AuthState.SignedIn::class.simpleName) }
.collect()
}
java.lang.IllegalStateException: ScreenModel not found: ui.login.LoginModel
at cafe.adriel.voyager.core.model.ScreenModelStore.getDependencyKey(ScreenModelStore.kt:39)
at com.netguru.common.ScreenExtensionsKt.getModelScope(ScreenExtensions.kt:29)
at ui.login.LoginModel.login(LoginModel.kt:19)
at ui.LoginModelTest$on login success state is SignIn$1.invokeSuspend(LoginModelTest.kt:40)
at ui.LoginModelTest$on login success state is SignIn$1.invoke(LoginModelTest.kt)
at ui.LoginModelTest$on login success state is SignIn$1.invoke(LoginModelTest.kt)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTestCoroutine$2.invokeSuspend(TestBuilders.kt:208)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTestCoroutine$2.invoke(TestBuilders.kt)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTestCoroutine$2.invoke(TestBuilders.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:55)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:112)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTestCoroutine(TestBuilders.kt:207)
at kotlinx.coroutines.test.TestBuildersKt.runTestCoroutine(Unknown Source)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invokeSuspend(TestBuilders.kt:167)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invoke(TestBuilders.kt)
at kotlinx.coroutines.test.TestBuildersJvmKt$createTestResult$1.invokeSuspend(TestBuildersJvm.kt:13)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at kotlinx.coroutines.test.TestBuildersJvmKt.createTestResult(TestBuildersJvm.kt:12)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest(TestBuilders.kt:166)
at kotlinx.coroutines.test.TestBuildersKt.runTest(Unknown Source)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest(TestBuilders.kt:154)
at kotlinx.coroutines.test.TestBuildersKt.runTest(Unknown Source)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest$default(TestBuilders.kt:147)
at kotlinx.coroutines.test.TestBuildersKt.runTest$default(Unknown Source)
at ui.LoginModelTest.on login success state is SignIn(LoginModelTest.kt:39)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
I can't use rememberScreenModel
or ScreenModelStore.getOrPut
in the test so that's why the error is thrown. Any idea how to work around this? @adrielcafe @terrakok @programadorthi @DevSrSouza
I'll already a little bit played around this library. And drew following navigation diagram of my expectations:
Maybe I'm not correctly described navigation flow or simple not understand jetpack compose (I'm working on it). And now I have several questions:
This is probably a feature request unless I missed something and certainly not a vital one.
Is there a way to restart a screen fresh?
For example in a tab screen I start a nested navigation, when pressing the same tab button I'd like to return to the home of that tab but in a fresh state (so not scrolled for example).
I can have the screen provide their nested navigator via an interface and for example use popUntilRoot
but as expected the root screen is restored in it's previous state (normal wanted default behavior).
In some cases I'd like to be able to restart that screen fresh
While testing my app with Voyager I noticed some of my AndroidView states were never being saved. Investigating that lead me to my LifecycleObserver's never filling the bundle contained in my rememberSavable variable. I then logged the states that the lifecycle observer goes through and noticed that only onCreate/OnStart/OnResume are ever called, and never the last 3.
Here are the test composables I used
@Composable
private fun rememberLoggingLifecycleObserver(): LifecycleEventObserver {
val bundle = rememberSaveable { Bundle() }
return remember {
LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_CREATE -> {
logcat("Lifecycle") { "OnCreate" }
logcat("Lifecycle") { bundle.keySet().joinToString() }
bundle.clear()
}
Lifecycle.Event.ON_START -> logcat("Lifecycle") { "OnStart" }
Lifecycle.Event.ON_RESUME -> logcat("Lifecycle") { "OnResume" }
Lifecycle.Event.ON_PAUSE -> logcat("Lifecycle") { "OnPause" }
Lifecycle.Event.ON_STOP -> {
logcat("Lifecycle") { "OnStop" }
bundle.putAll(bundleOf("Yes" to "1200"))
}
Lifecycle.Event.ON_DESTROY -> logcat("Lifecycle") { "OnDestroy" }
else -> throw IllegalStateException()
}
}
}
}
@Composable
fun SetupLoggerLifecycleObserver() {
val loggerObserver = rememberLoggingLifecycleObserver()
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(lifecycle) {
lifecycle.addObserver(loggerObserver)
onDispose {
// lifecycle.removeObserver(loggerObserver)
}
}
}
The fix (#55) have the side effect of now fully unloading the view models when navigating between tabs.
While the fix is normal and wanted, due to the current limitations of LazyList not proposing any proper way to delay the state restoration, this makes restoring scroll position insanely complex until they propose something.
It would be nice to have a way to opt out or a workaround for this use case.
I'm running into a weird issue with this library, that I haven't yet run into with other multiplatform libraries
I have a multiplatform module that targets android and desktop, which is setup like
import org.jetbrains.compose.compose
plugins {
id("com.android.library")
kotlin("multiplatform")
id("org.jetbrains.compose")
}
kotlin {
android { .. }
jvm("desktop") { .. }
sourceSets {
val commonMain by getting {
dependencies {
implementation(compose.runtime)
..
implementation(Adriel.voyagerNavigator)
implementation(Adriel.voyagerTabNavigator)
}
}
val androidMain by getting { .. }
val desktopMain by getting { .. }
}
}
However, when I try to declare any screens in the commonMain
, I get
If I move to the declaration to desktopMain
, it works just fine. Any idea why is this happening 🤔
I try use Screen + ScreenModel + Hilt.
I create Screen like this:
object SearchScreen : Screen {
@Composable
override fun Content() {
val screenModel = getScreenModel<SearchScreenModel>()
Log.e("!!!!","!!!!Use screenmodel: $screenModel")
SearchScreen(screenModel)
}
}
And my ScreenModel:
class SearchScreenModel @Inject constructor(
private val searchUseCase: SearchUseCase
) : ScreenModel {
//Some code
}
But when i rotate my phone screen model recreated. Shouldn't the screen model survive the configuration changes? OR am I doing something wrong?
> Could not resolve all artifacts for configuration ':modules:ui-bottomsheet-displayconfiguration:debugRuntimeClasspath'.
> Could not find org.jetbrains.compose.runtime:runtime:1.0.1-rc2.
Required by:
project :modules:ui-bottomsheet-displayconfiguration > cafe.adriel.voyager:voyager-core:1.0.0-beta14 > cafe.adriel.voyager:voyager-core-android-debug:1.0.0-beta14
project :modules:ui-bottomsheet-displayconfiguration > cafe.adriel.voyager:voyager-navigator:1.0.0-beta14 > cafe.adriel.voyager:voyager-navigator-android-debug:1.0.0-beta14
> Could not find org.jetbrains.compose.ui:ui:1.0.1-rc2.
Required by:
project :modules:ui-bottomsheet-displayconfiguration > cafe.adriel.voyager:voyager-core:1.0.0-beta14 > cafe.adriel.voyager:voyager-core-android-debug:1.0.0-beta14
project :modules:ui-bottomsheet-displayconfiguration > cafe.adriel.voyager:voyager-navigator:1.0.0-beta14 > cafe.adriel.voyager:voyager-navigator-android-debug:1.0.0-beta14
Is there a new way to handle the deps? I do not want jetbrain compose in the android app.
I have a list of items and a detail screen. Detail Screen is a compose function that takes a (post) parameter that I pass as an argument to the ViewModel.
val viewModel = getStateViewModel<PostDetailViewModel>(parameters = { parametersOf(args) })
The problem is that it looks like the viewModel is cached, and all the time it opens the first post (argument) that was followed.
In jetpack navigation works correctly.
Would be a useful function to close a nested navigation. For example, I have a flow for authorization with two screens, for entering a phone number and a password, when you call popUntilRoot() on the last screen, the screens are pulled out of the stack one by one and you can see how the keyboard blinks from the previous screens. It was convenient to have a mechanism to remove the entire chain at once.
Hi @adrielcafe, I wanted to show you my sample app I made with Voyager: https://github.com/JohnBuhanan/MVI-Public
I studied a dozen github repos and felt like yours was closest to what I wanted.
Three things I thought you might find interesting:
api
gradle module and an impl
gradle module. The impl
s can be commented out from settings.gradle
and app.gradle
to unload them from Android Studio indexing and gradle builds. This allows scaling for massively multi-module apps.Biggest thing I am thinking about now is best way to "navigate for result".
Hello, I'm trying to adopt your library into my pet project. I was faced with an issue, that the inner screen should consider padding from the parent screen.
Please take a look sample. I need to pass paddingValues into CurrentTab(). The Scaffold provides this value to prevent content overlapping.
TabNavigator(FlagsTab) { tabNavigator ->
Scaffold(
modifier = Modifier.systemBarsPadding(),
topBar = {},
content = { paddingValues ->
CurrentTab()
},
bottomBar = {
BottomNavigation {
TabNavigationItem(Tab1)
TabNavigationItem(Tab2)
}
}
)
}
My suggestion is to make Screen interface like this with default parameters:
interface Screen : Serializable {
val key: ScreenKey
get() = this::class.qualifiedName
?: error("Default ScreenKey not found, please provide your own key")
@Composable
fun Content(
modifier: Modifier = Modifier,
paddingValues: PaddingValues = PaddingValues()
)
}
Thanks a lot)
I use a Scaffold for my screens and I have my scaffold wrapping my screen content, with interfaces that handle different types of screens
Navigator(startScreen) {
CurrentScreenWithScaffold(it)
}
@Composable
fun CurrentScreenWithScaffold(navigator: Navigator) {
val currentScreen = navigator.lastItem
navigator.stateHolder.SaveableStateProvider(currentScreen.key) {
Scaffold(
topBar = {
if (currentScreen is AppBarScreen) {
currentScreen.AppBar()
}
},
bottomBar = {
BottomNav(
isVisible = currentScreen is BottomNavScreen
)
}
) {
Box(Modifier.padding(it)) {
currentScreen.Content()
}
}
}
}
I want to be able to animate only currentScreen.Content(), but I cant do that because of navigator.stateHolder.SaveableStateProvide, it can only be declared once but I also need it to wrap my currentScreen.AppBar() as it needs access to my ViewModel. I cannot find a way that satisfies my requirements with the current API.
Sometimes we need to display different icons or titles according to different situations, such as whether the tab is selected or not, so it is good to provide such interface.
Any plans for Compose Web Support? The big thing about navigating with web is browser url / paths need to be supported.
Hello, I'm currently trying to execute some logic based on current screen, but I can't find any callback that would give me current screen. Would be really nice if there is a way to expose flow of current screens. I would need such functionality for all navigators (tab, bottomSheet and regular navigator)
When androidx.lifecycle.viewmodel.compose.viewModel()
function is called inside plain ComponentActivity
or Fragment
, it uses ViewModelStoreOwner
of activity/fragment which implements HasDefaultViewModelProviderFactory
and returns SavedStateViewModelFactory
. This allows using ViewModel
that accepts SavedStateHandle
or Application
in constructor without any configuration.
If one then decides to migrate to Voyager library and use AndroidScreen
, they would encounter runtime exceptions because AndroidScreen's ViewModelStoreOwner
implementation doesn't implement HasDefaultViewModelProviderFactory
which makes viewModel()
use no arguments constructor.
AndroidScreen should use SavedStateViewModelFactory by default to match behaviour of ComponentActivity/Fragment (there also needs to be an API to pass Bundle
of default state to SavedStateViewModelFactory).
To reproduce this issue, just enable "Dont't keep activites" and use this test screen.
class TestScreen : Screen {
@Composable
override fun Content() {
var text by rememberSaveable {
mutableStateOf("")
}
Column {
TextField(value = text, onValueChange = { text = it })
}
}
}
rememberSaveable must work because some components used it under the hood to restore scroll position and other things.
Hi,
Testing this library and there's a small issue with current hilt integration if we do not want to use ScreenModel or are just testing before switching to them.
If we just add the voyager-hilt dependency then we end up with a dagger issue
error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends cafe.adriel.voyager.hilt.ScreenModelFactory>,javax.inject.Provider<cafe.adriel.voyager.hilt.ScreenModelFactory>> cannot be provided without an @Provides-annotated method.
public abstract static class SingletonC implements Application_GeneratedInjector,
^
java.util.Map<java.lang.Class<? extends cafe.adriel.voyager.hilt.ScreenModelFactory>,javax.inject.Provider<cafe.adriel.voyager.hilt.ScreenModelFactory>> is requested at
cafe.adriel.voyager.hilt.ScreenModelEntryPoint.screenModelFactories()
This can be worked around by just creating fake ScreenModel / ScreenModelFactory and a module to bind them but this should not be necessary or pointed in the doc if there's a better way to fix.
I'm still not fluent with multibinding so maybe there's something else I miss.
Hi.
I have a project that is targeting android and desktop.
I would like to use rememberScreenModel
in Screen but unfortunately, I'm getting this error
Cannot inline bytecode built with JVM target 11 into bytecode that is being built with JVM target 1.8. Please specify proper '-jvm-target' option
class Detail : Screen, KoinComponent {
@Composable
override fun Content() {
val model = rememberScreenModel<LoginModel> { get() }
val state: LoginState by model.state.collectAsState()
Button(onClick = { model.proccessData() }) {
Text("It Is Detail Screen ")
}
}
}
Any suggestions?
val viewModel by hiltViewModel<MyViewModel>() doesnt work inside the @Composable Content function
So the fix for the replace issue have now generated a worse issue.
java.lang.IllegalArgumentException: SavedStateProvider with the given key is already registered
at androidx.savedstate.SavedStateRegistry.registerSavedStateProvider(SavedStateRegistry.java:111)
at androidx.lifecycle.SavedStateHandleController.attachToLifecycle(SavedStateHandleController.java:50)
at androidx.lifecycle.SavedStateHandleController.create(SavedStateHandleController.java:70)
at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:67)
at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:84)
at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:109)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:171)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:139)
it will crash whenever you navigate again to a destination.
So push(Screen) pop() push(Screen) will crash.
As said in #17 the root issue is more about transitions and autodispose happening too early.
There's side effects too with ScreenModel that are disposed too early (even before the next screen is composed). This can corrupt what is displayed during transition and cause hard to diagnose side effects.
This is more a core issue for @adrielcafe than an hilt specific one. (The "fix" stills needs to be reverted)
While we can disable autodispose at the navigator level we have no way to built custom transitions that would do a proper dispose after the end of the transition.
I don't if that is intentional.Hilt integration only works with AndroidScreen
.
Also, docs seems incomplete... There is a sample project for that though.
I wonder is has ability to navigate not from Composable? For example if i want navigate from viewModel or UseCase class.
How i see that i can get navigator instance with LocalNavigator.currentOrThrow only from Composable function.
In the documentation here we are explained to use @parcelize and Parcelable however when doing so, the app throws an exception when you tap home, leave the app or open another Intent:
FATAL EXCEPTION: main Process: ca.redacted, PID: 10491 java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = ca....redacted) at android.os.Parcel.writeSerializable(Parcel.java:2125) at android.os.Parcel.writeValue(Parcel.java:1895) at android.os.Parcel.writeList(Parcel.java:1104) at android.os.Parcel.writeValue(Parcel.java:1844) at android.os.Parcel.writeList(Parcel.java:1104) at android.os.Parcel.writeValue(Parcel.java:1844) at android.os.Parcel.writeArrayMapInternal(Parcel.java:987) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620) at android.os.Bundle.writeToParcel(Bundle.java:1303) at android.os.Parcel.writeBundle(Parcel.java:1056) at android.os.Parcel.writeValue(Parcel.java:1813) at android.os.Parcel.writeArrayMapInternal(Parcel.java:987) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620) at android.os.Bundle.writeToParcel(Bundle.java:1303) at android.os.Parcel.writeBundle(Parcel.java:1056) at android.os.Parcel.writeValue(Parcel.java:1813) at android.os.Parcel.writeArrayMapInternal(Parcel.java:987) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620) at android.os.Bundle.writeToParcel(Bundle.java:1303) at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4969) at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8645) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130) Caused by: java.io.NotSerializableException: ca...redacted at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354) at android.os.Parcel.writeSerializable(Parcel.java:2120) at android.os.Parcel.writeValue(Parcel.java:1895) at android.os.Parcel.writeList(Parcel.java:1104) at android.os.Parcel.writeValue(Parcel.java:1844) at android.os.Parcel.writeList(Parcel.java:1104) at android.os.Parcel.writeValue(Parcel.java:1844) at android.os.Parcel.writeArrayMapInternal(Parcel.java:987) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620) at android.os.Bundle.writeToParcel(Bundle.java:1303) at android.os.Parcel.writeBundle(Parcel.java:1056) at android.os.Parcel.writeValue(Parcel.java:1813) at android.os.Parcel.writeArrayMapInternal(Parcel.java:987) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620) at android.os.Bundle.writeToParcel(Bundle.java:1303) at android.os.Parcel.writeBundle(Parcel.java:1056) at android.os.Parcel.writeValue(Parcel.java:1813) at android.os.Parcel.writeArrayMapInternal(Parcel.java:987) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620) at android.os.Bundle.writeToParcel(Bundle.java:1303) at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4969) at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8645) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
In fact the example given in the documentation doesn't work.
Versions used:
val voyager_version = 1.0.0-rc02
implementation "cafe.adriel.voyager:voyager-navigator:$voyager_version"
implementation "cafe.adriel.voyager:voyager-androidx:$voyager_version"
implementation "cafe.adriel.voyager:voyager-hilt:$voyager_version"
id org.jetbrains.kotlin.android version 1.7.0 apply false
ext {
compose_ui_version = 1.2.0-rc01
compose_compiler_version = 1.2.0
}
I did see this thread: ##59 but even when using something like:
@Parcelize
data class NavParam(
var firstName: String = "",
): Parcelable
data class NewScreen(
val navParam: NavParam
) : Screen {
}
The app still crashes.
It's look like ScreenModel logic working wrong. Overriden onDispose method in root screen not called when we leave this screen.
Hi. I am using Voyager to navigate in a project built on the Orbit MVI and Koin libraries. Orbit requires a ViewModel to store the container, so i can't use ScreenModel. I am injecting the viewModel using the getVievModel from Koin in @composable override fun Content(). The problem is that when i'm leaving the screen using the pop() function, the viewModel is not cleared over the app life cycle and if i open the same screen again i have the same viewModel with preloaded data. How to deal with this? Thank you in advance!
Something similar to Accompanist material navigation where we can define bottomsheet screens would be helpful to have. Bottomsheet Screens would show up in a bottom sheet layout with the last non bottom sheet Screen in the background.
Hi First of all, thank you for your amazing work
Add this feature to the library if possible
As per the title, it would be nice to remove it as appcompat is no more needed in a pure compose world.
So this is a tricky one.
Navigator(Screen1()) { navigator ->
SlideTransition(navigator)
}
class Screen1() : AndroidScreen {
@Composable
override fun Content() {
val viewModel = getViewModel<Screen1ViewModel>()
....
LocalNavigator.currentOrThrow.replace(Screen2())
}
}
Will lead to a crash
java.lang.IllegalArgumentException: SavedStateProvider with the given key is already registered
at androidx.savedstate.SavedStateRegistry.registerSavedStateProvider(SavedStateRegistry.java:111)
at androidx.lifecycle.SavedStateHandleController.attachToLifecycle(SavedStateHandleController.java:50)
at androidx.lifecycle.SavedStateHandleController.create(SavedStateHandleController.java:70)
at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:67)
at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:84)
at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:109)
From a quick look the viewmodel is properly cleared, but it seems Voyager tries to re access the SaveStateProvider later with the same key (obviously as it's the same composable).
There's no issue without enabling transitions, so this looks like some wrong order of calls, whatever access the savestate during the transition should do it before the full removal of the composable to avoid trying to reuse the key.
There's also no problem when just pushing a new screen. This works without using hitviewmodel.
I have cases where state restoration is either not desirable or not worth implementing the serialization for. Please provide a way to disable state restoration, either globally through Navigator
or on a per-screen basis through Screen
. If the app is closed from a screen that is not restorable it should use the initial screen on startup.
When Android destroys activity Voyager serialize screens and then restore them. But they now are different objects and Voyager uses them as "key" for "navigator.stateHolder.SaveableStateProvider" I think that is why Voyager can't restore state after activity recreation. (Same for tabNavigator.stateHolder.SaveableStateProvider(currentTab))
Hello and thanks for the library.
I am an engineer from Compose team at Google and wanted to mention that there is an expectation from the navigation library in Compose that this will save the inner state of the components which are currently in the backstack. Examples are the scrolling position of the LazyColumn should be restored when you go from screen with the list to the second screen and then go back to the list screen.
Other navigation libraries like the official one from androidx of Decompose support it as they use the special API designed to make is simpler: https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/SaveableStateHolder
Please let me know if you will have any questions, thanks!
In theory, the order of nested onBack functions should be from innermost up the chain. But in the current behavior, the opposite is true.
To repeat the steps, go to the nested navigation example, open 1 screen in each navigator and press the system back button 3 times.
My application has a case where it needs to pass a variable to a previous screen from the current screen. With fragments I have been using setFragmentResultListener api to do this, but with Voyager there is not a way to properly handle cases like this.
My suggestions:
I have an issue of AndroidScreens ViewModel being retained when activity is finished. This seems to only happen to root navigator screen. So when I am in my root view and press back, activity finishes, when I open the app again, screen gets the same ViewModel that has some state retained. Using regular Screen doesn't have this issue
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.