user2357113
user2357113

Reputation: 171

How to pass selected data from one screen to another using ViewModel in Jetpack Compose? (In backward)

I'm building an Android app with Jetpack Compose, and I'm facing an issue with passing selected data from one screen to another using a ViewModel.

Here's what I'm trying to achieve:

  1. Screen A has a click listener that opens Screen B.

  2. Screen B displays a list of items, and the user can select one item.

  3. Upon selecting an item in Screen B, I want to pass the selected data back to Screen A using a ViewModel.

    class MyViewModel : ViewModel() {
    private val _selectedMusicItem = MutableStateFlow<MusicItem?>(null)
    val selectedMusicItem = _selectedMusicItem.asStateFlow()
    
     fun updateSelectedMusicItem(musicItem: MusicItem) {
    _selectedMusicItem.value = musicItem
    }
    }
    

In Screen B, I'm updating the selected data in the ViewModel like this:

viewModel.updateSelectedMusicItem(selectedMusicItem)
navController.navigateUp()

And in Screen A, I'm trying to observe the selected data like this:

val selectedMediaListState by viewModel.selectedMusicItem.collectAsState()

However, the selected data isn't updating in Screen A when I navigate back from Screen B.

Can anyone help me understand how to correctly pass selected data from one screen to another using a ViewModel in Jetpack Compose? Any insights or suggestions would be greatly appreciated. Thank you!

Upvotes: 1

Views: 2198

Answers (2)

user2357113
user2357113

Reputation: 171

Here is the my version of answer yo my question. I've solved my problem without viewmodel it's one of the navigation approach

I've MusicItem.kt a data class.

I've used gson library to serialize and deserialized my data class for conversion of object to string.

here is my navgraph code

ScreenA(
        navController,
        viewModel,
        musicFromResult = it.savedStateHandle.get<String>("music") ?: "",
    )
}

I'm handling state using this varible

        musicFromResult = it.savedStateHandle.get<String>("music") ?: "",

Screen B where i want to update my data and go back to Screen A with updated data of screen B

 val gson = Gson()
    val jsonString = gson.toJson(musicItem)
    navController.apply {
        popBackStack()
        currentBackStackEntry
            ?.savedStateHandle
            ?.set(
                "music",
                jsonString.toString(),
            )
    }

here I'm converting my string data to object again to use it wisely in screen A

   @Composable
   fun  ScreenA(
    navController: NavHostController,
    viewModel: FinalPostViewModel,
    musicFromResult: String = "",
   ) {
    val gson = Gson()
    var deserializedMusicItem by remember { mutableStateOf<MusicItem?>(null) }
    try {
        deserializedMusicItem = gson.fromJson(musicFromResult, MusicItem::class.java)
     } catch (e: Exception) {
        deserializedMusicItem = null
        e.printStackTrace()
     }
  }

now i've data in this varible deserializedMusicItem i can use it in my screen a to get data.

Upvotes: 0

praveen kumar
praveen kumar

Reputation: 134

Here is my simple approach to pass the data.

(in your case , idk about your viewmodel implementation so)

In the Navigation page

@Composable
fun AppNavigation(){
 var selectedMusicItem by remember{ mutableStateOf<MusicItem?>(null) }

NavHost(navcontrol, startDestination = "screenA"){

 composable(route = "screenA"){
 ScreenA(navController,musicItem = selectedMusicItem)
 }

 composable(route = "screenB"){
 ScreenB(navController,selectedMusicItem = { music ->
  //call back from selected item
  selectedMusicItem = music
  })
 }

 }
}

and screenA and screenB composables are

 @Composable
fun ScreenA(navController: NavController,musicItem: MusicItem? ){
Button(onClick = { navController.navigate(route = "ScreenB") }) {

 }
}
@Composable
fun ScreenB(navController: NavController, selectedMusicItem: (MusicItem) -> 
Unit){
Button(
    onClick = {
        //here i pass the object as inside of callback
        selectedMusicItem(MusicItem.First)
        navController.navigate(route = "ScreenA")
    }
) {
    Text(text = "select First MusicItem")
}
}

using this solution wont cause any issues, but over using may cause CallbackHell , only use the solution if you can't find any other better solution.

Upvotes: 0

Related Questions