From 709428b9af556e9d7373b7158348000425fb6c68 Mon Sep 17 00:00:00 2001 From: Jan Skrasek Date: Fri, 27 Oct 2023 12:08:59 +0200 Subject: [PATCH 1/2] fix NPE in result processing when nav controller has not backstack entry intialized yet This fixes some kind of race condition we are not able to properly reproduce. --- .../navigationcompose/typed/ResultSharing.kt | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/com/kiwi/navigationcompose/typed/ResultSharing.kt b/core/src/main/kotlin/com/kiwi/navigationcompose/typed/ResultSharing.kt index e8a85da..35e7c51 100644 --- a/core/src/main/kotlin/com/kiwi/navigationcompose/typed/ResultSharing.kt +++ b/core/src/main/kotlin/com/kiwi/navigationcompose/typed/ResultSharing.kt @@ -2,9 +2,14 @@ package com.kiwi.navigationcompose.typed import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.navigation.NavController +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json @@ -31,9 +36,23 @@ public inline fun , reified R : Any> Composable navController: NavController, noinline block: (R) -> Unit, ) { + // The async waiting workarounds situations when navController.currentBackStackEntry is null + // ~ not yet populated. + val currentDestinationState = remember(navController) { + mutableStateOf(navController.currentDestination) + } + val currentDestination = currentDestinationState.value + if (currentDestination == null) { + LaunchedEffect(navController) { + val currentBackStackEntry = navController.currentBackStackEntryFlow.filterNotNull().first() + currentDestinationState.value = currentBackStackEntry.destination + } + return + } + ResultEffectImpl( navController = navController, - currentRoute = navController.currentDestination!!.route!!, + currentRoute = currentDestination.route!!, // routes are always not null in Nav Compose destinationSerializer = serializer(), resultSerializer = serializer(), block = block, From e0b7467107db9d445186409a10902895b0eaa6d3 Mon Sep 17 00:00:00 2001 From: Jan Skrasek Date: Fri, 27 Oct 2023 12:14:18 +0200 Subject: [PATCH 2/2] update readme for Result sharing --- .../typed/demo/screens/NameEditDialog.kt | 4 ++-- readme.md | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/demo/src/main/kotlin/com/kiwi/navigationcompose/typed/demo/screens/NameEditDialog.kt b/demo/src/main/kotlin/com/kiwi/navigationcompose/typed/demo/screens/NameEditDialog.kt index c6c64ab..2196608 100644 --- a/demo/src/main/kotlin/com/kiwi/navigationcompose/typed/demo/screens/NameEditDialog.kt +++ b/demo/src/main/kotlin/com/kiwi/navigationcompose/typed/demo/screens/NameEditDialog.kt @@ -20,9 +20,9 @@ internal fun NameEditDialog(navController: NavController) { NameEdit( onNameSave = { name -> navController.setResult(ProfileDestinations.NameEditDialog.Result(name)) - navController.navigateUp() + navController.popBackStack() }, - onDismiss = navController::navigateUp, + onDismiss = navController::popBackStack, ) } diff --git a/readme.md b/readme.md index deee25e..866fe66 100644 --- a/readme.md +++ b/readme.md @@ -150,7 +150,9 @@ Another set of functionality is provided to support the result sharing. First, d ```kotlin import com.kiwi.navigationcompose.typed.Destination +import com.kiwi.navigationcompose.typed.DialogResultEffect import com.kiwi.navigationcompose.typed.ResultDestination +import com.kiwi.navigationcompose.typed.setResult sealed interface Destinations : Destination { @Serializable @@ -164,7 +166,7 @@ sealed interface Destinations : Destination { @Composable fun Host(navController: NavController) { - ComposableResultEffect(navController) { result: Destinations.Dialog.Result -> + DialogResultEffect(navController) { result: Destinations.Dialog.Result -> println(result) // process the result } @@ -175,4 +177,16 @@ fun Host(navController: NavController) { Text("Open") } } + +@Composable +fun Dialog(navController: NavController) { + Button( + onClick = { + navController.setResult(Destinations.Dialog.Result(something = 42)) + navController.popBackStack() + } + ) { + Text("Set and close") + } +} ```