Status Update
Comments
lp...@google.com <lp...@google.com> #2
Respectfully in regards to marking this duplicate. It is not quite a duplicate. That referenced ticket is part of SafeArgs, while this request is for typesafe navigation. It was marked as "infeasible", yet w'ere talking about two completely different bases.
In the last
This requires support for doubles in NavInflater whose values are all backed by the TypedValue class, which unfortunately does not support Double values so we cannot go forward with this.
When in fact this is trivial to implement in compose typesafe navigation and seems to be a major omission:
object DoubleNavType : NavType<Double>(isNullableAllowed = false) {
@Suppress("DEPRECATION")
override fun get(bundle: Bundle, key: String): Double = bundle[key] as Double
override fun parseValue(value: String): Double {
// At runtime the d suffix is optional, contrary to the Safe Args plugin.
// This is in order to be able to parse double numbers passed as deep link URL
// parameters
var localValue = value
if (value.endsWith("d")) {
localValue = localValue.substring(0, value.length - 1)
}
return localValue.toDouble()
}
override fun put(bundle: Bundle, key: String, value: Double) {
bundle.putDouble(key, value)
}
}
/**
* Convience class to allow for double navigation types by turning them into strings and back.
*/
object NullableDoubleNavType : NavType<Double?>(isNullableAllowed = true) {
@Suppress("DEPRECATION")
override fun get(bundle: Bundle, key: String): Double? = bundle[key] as? Double?
override fun parseValue(value: String): Double? {
if (value == "null") return null
return DoubleNavType.parseValue(value)
}
override fun put(bundle: Bundle, key: String, value: Double?) {
if (value == null) {
bundle.putSerializable(key, null)
} else {
DoubleNavType.put(bundle, key, value)
}
}
}
// in a route
typeOf<Double>() to DoubleNavType,
typeOf<Double?>() to NullableDoubleNavType,
lp...@google.com <lp...@google.com> #3
Similar to the work done in @Serializable
can support, I think we can do something similar here to support doubles.
ti...@google.com <ti...@google.com> #4
jb...@google.com <jb...@google.com> #5
Branch: androidx-main
commit b9a16ee818ee85ae088bcf8787014eb801b10204
Author: Clara Fok <clarafok@google.com>
Date: Fri Aug 30 15:32:12 2024
Add built-in safe args support for non-nullable Double
Test: ./gradlew navigation:navigation-common:cC
Test: ./gradlew navigation:navigation-common:test
Test: ./gradlew navigation:navigation-runtime:cC
Bug: 359245753
Relnote: "Navigation in safe args now has built-in support for non-nullable Double type."
Change-Id: I570ebef5242e1bc3e8ee3f5d58b4aa2d677abaee
M navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteDecoderTest.kt
M navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
M navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
M navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
M navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavTypeConverterTest.kt
M navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
jb...@google.com <jb...@google.com> #6
Branch: androidx-main
commit b1c0e53c08a0c80fd8134e2206d70feaf49a6de6
Author: Clara Fok <clarafok@google.com>
Date: Fri Aug 30 15:45:18 2024
Add built-in safe args support for nullable Double
Test: ./gradlew navigation:navigation-common:cC
Test: ./gradlew navigation:navigation-common:test
Test: ./gradlew navigation:navigation-runtime:cC
Bug: 359245753
Relnote: "Navigation in safe args now has built-in support for nullable Double type."
Change-Id: Ibc4c02fa77e416c1c007209fb358a0d1c34389db
M navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteDecoderTest.kt
M navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
M navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
M navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
M navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavTypeConverterTest.kt
M navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
ti...@google.com <ti...@google.com> #8
Branch: androidx-main
commit 2bbd62e9ba772d50bf4da98ada024a798257af28
Author: Clara Fok <clarafok@google.com>
Date: Thu Sep 19 04:47:53 2024
Add built-in safe args support for List<Double>
Test: ./gradlew navigation:navigation-common:cC
Test: ./gradlew navigation:navigation-common:test
Test: ./gradlew navigation:navigation-runtime:cC
Bug: 359245753
Relnote: "Navigation with safe args now has built-in support for nullable List<Double>"
Change-Id: I5bed4745773efa2e8a57fcdacc77cc16b1845067
M navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteDecoderTest.kt
M navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
M navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
M navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
M navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavTypeConverterTest.kt
M navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
jb...@google.com <jb...@google.com> #9
Fixed internally and will be available in navigation 2.8.2
.
ti...@google.com <ti...@google.com> #10
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.annotation:annotation-wasm-js:1.9.0-rc01
androidx.annotation:annotation-watchosdevicearm64:1.9.0-rc01
androidx.navigation:navigation-common:2.8.2
androidx.navigation:navigation-runtime:2.8.2
ti...@google.com <ti...@google.com> #11
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.navigation:navigation-common:2.9.0-alpha01
jb...@google.com <jb...@google.com> #12
Yep same one:
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
I Skipped 40 frames! The application may be doing too much work on its main thread.
D Installing profile for com.andreirozov.sample
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
D navigating to screen2
D Current entry being composed in AnimatedContent screen1
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is STARTED
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen2
E screen2 entry state in Scaffold STARTED
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is STARTED
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Inside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is RESUMED
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Inside of conditional
D transition currentState: screen2
D transition targetState: screen2
E screen2 entry state in Scaffold RESUMED
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is RESUMED
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Inside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is RESUMED
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Inside of conditional
D transition currentState: screen2
D transition targetState: screen2
E screen2 entry state in Scaffold RESUMED
D navigating to screen1
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is CREATED
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen1
I Background young concurrent copying GC freed 53694(1896KB) AllocSpace objects, 0(0B) LOS objects, 25% free, 4767KB/6441KB, paused 7.336ms total 70.605ms
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
E screen2 entry state in Scaffold DESTROYED
E FATAL EXCEPTION: main
Process: com.andreirozov.sample, PID: 2772
java.lang.IllegalStateException: You cannot access the NavBackStackEntry's ViewModels after the NavBackStackEntry is destroyed.
at androidx.navigation.NavBackStackEntry.getViewModelStore(NavBackStackEntry.kt:202)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(ViewModel.kt:206)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.get$default(ViewModel.kt:195)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(ViewModel.kt:120)
at com.andreirozov.sample.ui.standings.screen2.Screen2TabKt.AnotherScreen(Screen2Tab.kt:84)
at com.andreirozov.sample.ui.AppNavGraphKt$AppNavGraph$5$1$1$2.invoke(AppNavGraph.kt:94)
at com.andreirozov.sample.ui.AppNavGraphKt$AppNavGraph$5$1$1$2.invoke(AppNavGraph.kt:89)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:162)
at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2435)
at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2703)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3326)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3304)
at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3304)
at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:3269)
at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:771)
at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1047)
at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:124)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:541)
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: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)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@7ebb51b, androidx.compose.ui.platform.MotionDurationScaleImpl@e6c86b8, StandaloneCoroutine{Cancelling}@f919c91, AndroidUiDispatcher@7da07f6]
I Sending signal. PID: 2772 SIG: 9
ti...@google.com <ti...@google.com> #13
Thanks Jeremy.
The logging in #12 also confirms Scaffold's behavior of recomposing after being removed from composition. Marking this a dup of
mo...@gmail.com <mo...@gmail.com> #14
ap...@google.com <ap...@google.com> #15
Branch: androidx-main
commit aeb4b9fc4ddeca49fcb2812ce828cc7be11beb30
Author: Jeremy Woods <jbwoods@google.com>
Date: Tue Aug 08 17:15:11 2023
Fix issue with changing state as part of composition
We should not change state as part of composition as that can cause
inconsistent behavior when reading state. We should instead only ever
change state in an Effect.
RelNote: "Fixed an issues in Navigation with Compose where when using a
Scaffold it was possible to get an error trying to access a DESTROYED
ViewModel"
Test: Added test in NavHostTest
Bug: 268422136
Change-Id: I1dc115fc2b454d68c1a23c78e72707d6d4c739a5
M navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
M navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
na...@google.com <na...@google.com> #16
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.navigation:navigation-compose:2.8.0-alpha01
pa...@gmail.com <pa...@gmail.com> #17
Hello,
these changes in fact introduced in 2.7.1
are creating issues in our architecture. Basically we are no longer able to receive Lifecycle.Event.ON_DESTROY
as it is now triggered after onDispose
where we are unsubscribing a lifecycle observer. (Sample attached)
fun attachLifeCycleObserver(onDestroy:() -> Unit) {
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(lifecycle) {
val eventObserver = LifecycleEventObserver { source, event ->
println("PACMAC -- event:${event.name} targetState:${event.targetState.name} source:${source.toString()}")
if (event == Lifecycle.Event.ON_DESTROY) {
onDestroy()
}
}
lifecycle.addObserver(eventObserver)
onDispose {
// We unsubscribe here but since 2.7.1 we are no longer receiving ON_DESTROY as it is actually triggered after onDispose
lifecycle.removeObserver(eventObserver)
}
}
}
We are using onDestroy
to run some cleanup but only if the nav destination is popped of the backstack.
Description
This is fromhttps://github.com/google/accompanist/issues/1487 :
I haven't been able to reproduce it without a lot of experimental APIs along with Navigation. But essentially given the following:
There are cases where the areas end up being composed in this order:
1, 3, 2
withAnimatedContent
completing the transition and then the Scaffold composing its content.The expectation was that it would always be
1, 2, 3
with the composition of theScaffold
content completing before the callback to area 3.Is the expectation correct? Or does
AnimatedContent
completion have some priority over theScaffold
?This could also be something going on in
Scaffold
, just wondering what the expectations are here.