|
/** |
|
* Sets the result to be obtained by [NavResultCallback] attached to [targetBackStackEntry] |
|
*/ |
|
abstract class NavResultPusher<T>( |
|
private val targetBackStackEntry: NavBackStackEntry, |
|
private val key: String |
|
) { |
|
open fun setResult(result: T) = targetBackStackEntry.savedStateHandle.set(key, result) |
|
} |
|
|
|
/** |
|
* Observes results passed from a nav destination to this [NavBackStackEntry]. |
|
* Delivers the result [ON_RESUME] to handle both fullscreen and dialog origins. |
|
* |
|
* Alternative to savedStateHandle.getLiveData which redelivers the old value despite it being cleared |
|
* and which doesn't handle dialog destinations well |
|
* (see: https://developer.android.com/guide/navigation/navigation-programmatic#additional_considerations) |
|
*/ |
|
abstract class NavResultCallback<T>( |
|
private val currentBackStackEntry: NavBackStackEntry, |
|
private val key: String |
|
) { |
|
|
|
private val flow = MutableStateFlow<T?>(null) |
|
|
|
open fun observeResult(): Flow<T?> { |
|
|
|
val observer = LifecycleEventObserver { _, event -> |
|
if (event == ON_RESUME && currentBackStackEntry.savedStateHandle.contains(key)) { |
|
flow.tryEmit(currentBackStackEntry.savedStateHandle.get<T>(key)!!) |
|
} |
|
} |
|
|
|
return flow |
|
.onStart { currentBackStackEntry.lifecycle.addObserver(observer) } |
|
.onCompletion { currentBackStackEntry.lifecycle.removeObserver(observer) } |
|
} |
|
|
|
fun clearResult() { |
|
currentBackStackEntry.savedStateHandle.remove<Boolean>(key) |
|
flow.tryEmit(null) |
|
} |
|
|
|
} |
|
|
|
// ===USAGE=== |
|
private const val DELETED_ID = "deleted_id" |
|
|
|
//this should be injected into the viewmodel of the the Composable started for result, e.g. some DeleteItemScreen |
|
//when the item is deleted and just before popBackStack, the viewmodel should call pusher.setResult(deletedId) |
|
class DeletedIdResultPusher( |
|
targetBackStackEntry: NavBackStackEntry |
|
) : NavResultPusher<String>(targetBackStackEntry, DELETED_ID) |
|
|
|
//this should be injected into the viewmodel of the the Composable that navigates to DeleteItemScreen for result |
|
//it can e.g. callback.observeResult().collectAsState in the composable to show a Snackbar and when the snackbar is dimissed |
|
//it can call callback.clear() to get rid of the result |
|
class DeletedIdResultCallback( |
|
currentBackStackEntry: NavBackStackEntry |
|
) : NavResultCallback<String>(currentBackStackEntry, DELETED_ID) |