jair
jair

Reputation: 1

ViewModel Structure not maintaining variable values when called in seperate Jetpack Compose Composables

In the DiscoverCountries composable page, there is a button composable that updates the value of the selectedContinent string member of the ContinentsViewModel ViewModel() class. I am trying to use the updated string in a different composable function in a different file, but the default empty string is being maintained.

When this specific button is clicked, it passes the viewModel1.selectContinent("North America") function to execute, changing the value of the string in the viewmodel.

class ContinentsViewModel : ViewModel() {
    val selectedContinent: MutableState<String> = mutableStateOf("")

    fun selectContinent(continent: String) {
        selectedContinent.value = continent
        Log.d("ViewModel", "Continent selected: $continent")

    }
}
item(){
                    val pdeCard3 = Card("Some Title", "Some Location", 123)
                    pdeCard3.ContinentsCard(navController,
                        "North America",
                        onContinentSelected = { viewModel1.selectContinent("North America") })
                }

Here is the relevant code snippet for the function definition:

    @Composable
    fun ContinentsCard(navController: NavHostController, continentName: String, onContinentSelected: () -> Unit) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .height(67.dp)
                .clickable{
                    onContinentSelected()
                    navController.navigate(ScreenView.ContinentsCountriesPage.route)
                }

The button does correctly navigate to the ContinentsCountriesPage, but the "North America" string in the viewmodel is not maintained, it is seen as an empty string from the destination composable.

@Composable
fun ContinentsCountriesPage(navController: NavHostController){
    Log.d("ViewModelTest", "Page Opened")

    val viewModel1: ContinentsViewModel = viewModel()
    val selectedContinent = viewModel1.selectedContinent.value
    Log.d("ViewModel", "Continent selected: $selectedContinent")
    Log.d("ViewModelTest", "ContinentsCountries ViewModel: ${viewModel1.hashCode()}")

I have tried console logging the value of the selectedContinent, the value is correctly being updated in the DiscoverCountries code file, but an empty string in the ContinentsCountriesPage file. I have ensured the views are under the same Navigation Host controller. When logging the hashCode of the viewmodels in each file, they are different, I'm not sure if they are supposed to be the same hashCode.

Upvotes: 0

Views: 54

Answers (2)

Heberth Deza
Heberth Deza

Reputation: 615

In your ViewModel class, try to replace this line:

    val selectedContinent: MutableState<String> = mutableStateOf("")

For this one:

    var selectedContinent: MutableState<String> = mutableStateOf("")
     private set

This means that your variable is only available for read outside the ViewModel, but read & write inside ViewModel.

Upvotes: 0

Jan Itor
Jan Itor

Reputation: 4256

You may consider passing the string as a navigation argument as described here: Navigate with arguments.

Or you can create an app level ViewModel. To do so, you can pass your ComponentActivity as a ViewModelStoreOwner to viewModel function. To find the parent activity you can use this function (source):

fun Context.findActivity(): ComponentActivity? = when (this) {
    is ComponentActivity -> this
    is ContextWrapper -> baseContext.findActivity()
    else -> null
}

val appViewModel: AppViewModel = viewModel(LocalContext.current.findActivity()!!)

Upvotes: 0

Related Questions