Reputation: 171
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:
Screen A has a click listener that opens Screen B.
Screen B displays a list of items, and the user can select one item.
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
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
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