Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SavedStateHandle on ViewModel not contain navigation argument #3730

Closed
CMFerrer opened this issue Feb 5, 2023 · 2 comments
Closed

SavedStateHandle on ViewModel not contain navigation argument #3730

CMFerrer opened this issue Feb 5, 2023 · 2 comments

Comments

@CMFerrer
Copy link

CMFerrer commented Feb 5, 2023

I'm trying to inject SavedStateHandle. I am using jetpack compose and navigation

Here is my NavHost:

AnimatedNavHost(
    navController = navController,
    startDestination = "main_route", route = "Parent"
) {
     composable(
        route = "main_route",
        enterTransition = {...},
        exitTransition = {...}
    )
    {
        MainScreen()
     }

    composable(
        route = "route_A/{text}",
        arguments = listOf(navArgument(name = "text") {
            type = NavType.BoolType

        }),
        enterTransition = {...},
        exitTransition = {...}
    )
    {
        val parentEntry = remember(it) { navController.getBackStackEntry("Parent") }
        val sharedViewModel = hiltViewModel<SharedViewModel>(parentEntry)
        val state by sharedViewModel.stateA.collectAsStateWithLifecycle()

        CaloScreen(
            state,
            ) { navigate ->
                navController.navigate(route = "route_detail/$navigate")
            }
     }

    composable(
        route = "route_B/{text}",
        arguments = listOf(navArgument(name = "text") {
            type = NavType.BoolType

        }),
        enterTransition = {...},
        exitTransition = {...}
    )
    {
        val parentEntry = remember(it) { navController.getBackStackEntry("Parent") }
        val sharedViewModel = hiltViewModel<SharedViewModel>(parentEntry)
        val state by sharedViewModel.stateB.collectAsStateWithLifecycle()

        CaloScreen(
            state,
            ) { navigate ->
                navController.navigate(route = "route_detail/$navigate")
            }
     }

    composable(
        route = "route_detail/{id}",
        arguments = listOf(navArgument(name = "id") {
            type = NavType.StringType

        })
    )
    {
        val detailViewModel = hiltViewModel<DetailViewModel>()
        val state by detailViewModel.state.collectAsStateWithLifecycle()

        DeloScreen(state)
    }
}

Here is SharedViewModel:

@HiltViewModel
class SharedViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val repo: DataRepository
) : ViewModel() {

    private val _stateA = MutableStateFlow(value = CaloState())
    val stateA = _stateA.asStateFlow()

    private val _stateB = MutableStateFlow(value = CaloState())
    val stateB = _stateB.asStateFlow()

    init {
        val argument = savedStateHandle.get<Boolean>("text")
        ...
        ...
    }
    ...
}

savedStateHandle always return null, not contain any keys. This happens in the SharedViewModel but not in the detailviewmodel. When I don't pass the parentEntry to the sharedviewmodel of route_A and route_B it returns the value passed by argument but they would no longer share the same ViewModel instance.
I also tried to make the argument a String type but it didn't work anyway.

Hilt version use -> 2.44.2

I'm new to Android, thanks in advance

@kuanyingchou
Copy link
Collaborator

Hi, @CMFerrer , thanks for reporting. It seems that navigation is also using the same underlying mechanism to implement their nav arguments, and here your're trying to write the text arg in navigation and read it in a viewmodel. This behavior is probably not something you want to depend on. For now it may be better to also read the nav arg in navigation with NavBackStackEntry#arguments and pass that to the viewmodel after the hiltViewModel() call. We're still working on the feature to pass something manually to hiltViewModel() so it should get better in the future.

@kuanyingchou
Copy link
Collaborator

Sorry I just found that passing data though navArgs and reading them from savedStateHandle in ViewModels is what Navigation recommends. Please ignore my comment above. However, reading the navArgs in a ViewModel that's scoped to Parent is not going to work as the args are stored separately. This is a Navigation issue instead of a Hilt issue. For example, remove Hilt from the scene and replace hiltViewModel() with androidx.lifecycle.viewmodel.compose.viewModel() and the issue remains. In this case you may have to wire the data manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants