Alexander Karkossa
05/15/2021, 11:26 AMnavigation-compose
and hiltViewModel
. Any ideas, why my example never gets a result?
The savedStateHandle
within my viewModel
never gets the result value. I only get the result directly from navBackStackEntry.savedStateHandle
.
Example in second postAlexander Karkossa
05/15/2021, 11:26 AM@Composable
fun DemonstartionApp() {
val navHostController = rememberNavController()
Surface(modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
) {
NavHost(navController = navHostController, startDestination = "screen1") {
composable(
route = "screen1"
) { navBackStackEntry ->
val viewModel = viewModel<Screen1ViewModel>(factory = HiltViewModelFactory(LocalContext.current, navBackStackEntry))
val id by viewModel.selectedId.observeAsState()
val idFromNavBackStackEntry: String? = remember(navBackStackEntry.savedStateHandle) {
navBackStackEntry.savedStateHandle.get<String>("selectedId")
}
Surface {
Column {
Text(text = id ?: "No id from hiltViewModel savedStateHandle")
Text(text = idFromNavBackStackEntry ?: "No id from navBackStackEntry savedStateHandle")
Button(onClick = {
navHostController.navigate("screen2")
}) {
Text(text = "Select ID")
}
}
}
}
composable(
route = "screen2"
) {
Surface{
Column {
Button(onClick = {
navHostController.previousBackStackEntry?.savedStateHandle?.set("selectedId", "ID_1")
navHostController.popBackStack()
}) {
Text(text = "ID 1")
}
Button(onClick = {
navHostController.previousBackStackEntry?.savedStateHandle?.set("selectedId", "ID_2")
navHostController.popBackStack()
}) {
Text(text = "ID 2")
}
}
}
}
}
}
}
@HiltViewModel
class Screen1ViewModel @Inject constructor(
savedStateHandle: SavedStateHandle
): ViewModel() {
val selectedId: LiveData<String> = savedStateHandle.getLiveData("selectedId")
}
Alexander Karkossa
05/15/2021, 12:33 PMsavedStateHandle
within the viewModel
and navBackStackEntry.savedStateHandle
in screen1 are not the same objects. Thats the reason, why the LiveData<String>
in the viewModel
never triggerts. @Ian Lake Is that the way it's supposed to be?Ian Lake
05/15/2021, 1:18 PMnavBackStackEntry.savedStateHandle
is not used in your own custom ViewModel (every ViewModel gets its own SavedStateHandle)
, so it is expected that those are completely different objectsIan Lake
05/15/2021, 1:21 PMhiltNavGraphViewModel()
method for retrieving Hilt provided ViewModels. Those APIs would allow you to pass navHostController.previousBackStackEntry
in, thus giving your other destination direct access to your same Screen1ViewModel
instanceAlexander Karkossa
05/15/2021, 1:37 PMhiltNavGraphViewModel(
navHostController.previousBackStackEntry)
and set the value directly to it?Ian Lake
05/15/2021, 1:38 PMSavedStateHandle,
which seems like what you are trying to do?Alexander Karkossa
05/15/2021, 1:44 PMnavHostController.previousBackStackEntry?.savedStateHandle?.set("selectedPictureId", pictureId)
and the previous destionation get it from navBackStackEntry
and set it to its viewModel.
val selectedPictureId: String? = remember(navBackStackEntry.savedStateHandle) {
navBackStackEntry.savedStateHandle.get<String>("selectedPictureId")
}
selectedPictureId?.let {
viewModel.setSelectedPictureId(it)
}
Alexander Karkossa
05/15/2021, 1:47 PMAlexander Karkossa
05/15/2021, 1:54 PMLaunchedEffect(navBackStackEntry.savedStateHandle) {
navBackStackEntry.savedStateHandle.get<String>("selectedPictureId")?.let {
viewModel.setSelectedPictureId(it)
}
}
Ian Lake
05/15/2021, 2:12 PMval selectedPictureId = navBackStackEntry.savedStateHandle.getLiveData<String>("selectedPictureId").observeAsState()
if (selectedPictureId == null) {
// Show "select a picture UI
} else {
val dataFromViewModel = viewModel.getPicture(selectedPictureId).collectAsState()
// Now use the data to fill in your UI
}
Alexander Karkossa
05/15/2021, 2:20 PMIan Lake
05/15/2021, 4:25 PMLaunchedEffect
route might be better for processing the selected item. I'd make sure you call savedStateHandle.remove("selectedPictureId")
after processing it if you are treating it like an event you need to process only once per selection and not like state