Search code examples
androidandroid-jetpack-composeparcelablejetpack-compose-navigation

How to pass an object through Navigation in Android compose?


I am using Navigation Compose, and I am trying to pass an entire object to the next screen, So I am using a shared ViewModel through hilt, and created a mutablestate variable of that object and want to get its value in the next screen. Like This

var campaign = mutableStateOf<Campaign?>(null)
private set

fun addCampaign(campaign: Campaign){
  this.campaign.value = campaign
}

where Campaign is just a data-class.

In my Screen in Navigation

 LazyColumn (
        content =
        {
            items(viewModel.campaignListCurrent){ campaign ->
                CampaignItem(
                    image = campaign.brand?.image ?: "",
                    title = campaign.name ,
                    id = campaign.id ,
                    description =campaign.description ,
                    date =campaign.createdAt ) {

                    viewModel.addCampaign(
                        CampaignsViewModel.Campaign(...) // the "campaign" object is used to fill this   
                    )
                    Timber.tag("CampaignObject").v(viewModel.campaign.value.toString())
                    viewModel.changed = true
                    navController.navigate(Screen.CampaignDetailsScreen.route)
                }
            }
        })

I can see when I log the data that it was stored successfully. yet in the next screen when I get the data from the same ViewModel, it's null. even though its a mutablestate and supposed to change its value and be observables, I don't think I get mutablestate behavior at all, and any link for a proper explanation for it will be appreciated.

  val campaign by remember { viewModel.campaign }

    Timber.v("CampaignDetailsScreen2: " + campaign.toString())

can someone explain to me why it doesn't work here, even if I used Launched effect?? why doesn't change its value here?


Solution

  • I managed to solve this by Creating a SharedViewModel instance in the NavGraph to make sure it is the same Instance used in both composable functions.

        @Composable
    fun CampaignsScreen(
        navController : NavController,
        viewModel: CampaignsViewModel
    ) {
    

    ....

        @Composable
    fun CampaignDetailsScreen(
        navController : NavController,
        viewModel: CampaignsViewModel
    ) {
    

    and in my NavGraph I passed the ViewModel as HiltViewmodel in the constructor and from it to the two screens

        @Composable
        fun SetUpNavGraph (navController : NavHostController,
                           campaignViewModel: CampaignsViewModel = hiltViewModel()) {
    ...........
        
                    loginNavGraph(navController = navController)
            
                    homeNavGraph(navController = navController, campaignsViewModel = campaignViewModel)
                }
            }
    

    and in the home NavGraph I passed the parametere and its the same Instance in both

    fun NavGraphBuilder.homeNavGraph(
        navController : NavHostController,
        campaignsViewModel: CampaignsViewModel
    ){
    
        navigation(startDestination = Screen.HomeScreen.route, route = HOME_GRAPH_ROUTE) {
          ................
            composable(Screen.Campaigns.route){
                CampaignsScreen(navController = navController, viewModel = campaignsViewModel)
            }
            composable(Screen.CampaignDetailsScreen.route){
                 CampaignDetailsScreen(navController, viewModel = campaignsViewModel)
            }
    
        }
    }
    

    You can use backstackEntry and PreviouseBackStack to solve this but it won't work if you have a JSON Object within your object as you will need to Serialize and Deserialize this object manually and it will take more time and code to achieve the same goal for a shared ViewModel to pass Parclized object. until Google Solve passing Parclized objects in Compose Navigation I recommend backStack Entry if you don't have a JSON object within your paralyzed object and if you do, use a shared ViewModel between the tow composable function.