I update the uiState in the model by the updateSelectedCategory(category = selectedCategory)
function, I checked through the logs int the viewmodel, everything is fine, it is updated. But, when I check this in the WhehereToGoApp
compose function, the parameter uiState.currentPlacesList
is not updated, why? I think I'm somehow not collecting the state correctly, but I can't figure out how to fix it.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WhereToGoApp() {
val viewModel = WhereToGoViewModel()
val uiState = viewModel.uiState.collectAsState()
val navController = rememberNavController()
val isCategoryScreen = (navController.currentBackStackEntryAsState().value?.destination?.route
?: WhereToGoAppScreens.Categories.name) == WhereToGoAppScreens.Categories.name
Scaffold(
topBar = {
WhereToGoAppBar( // todo
title = if (isCategoryScreen) {
"Select Category "
} else {
uiState.value.selectedCategory.title
},
showNavigationIcon = !isCategoryScreen,
onBackButtonClick = {
navController.navigateUp()
}
)
}
) { contentPadding ->
NavHost(
navController = navController,
startDestination = WhereToGoAppScreens.Categories.name
) {
composable(route = WhereToGoAppScreens.Categories.name) {
CategoriesScreen(
categoryList = LocalDataProvider.getCategories(),
onItemClick = { selectedCategory ->
viewModel.updateSelectedCategory(category = selectedCategory)
navController.navigate(WhereToGoAppScreens.Places.name)
},
paddingValues = contentPadding
)
}
composable(route = WhereToGoAppScreens.Places.name) {
PlacesScreen(
places = uiState.value.currentPlacesList,
onItemClick = { selectedPlace ->
viewModel.updateSelectedPlace(place = selectedPlace)
navController.navigate(WhereToGoAppScreens.PlaceDetails.name)
},
paddingValues = contentPadding
)
}
composable(route = WhereToGoAppScreens.PlaceDetails.name) {
PlaceDetailScreen(
place = uiState.value.selectedPlace,
paddingValues = contentPadding
)
}
}
}
}
ViewModel code
class WhereToGoViewModel : ViewModel() {
private val _uiState = MutableStateFlow(WhereToGoUiState())
val uiState: StateFlow<WhereToGoUiState> = _uiState.asStateFlow()
fun updateSelectedCategory(category: Category) {
_uiState.update { uiState ->
uiState.copy(
selectedCategory = category,
currentPlacesList = LocalDataProvider.getPlaces().filter {
it.category == category
}
)
}
}
fun updateSelectedPlace(place: Place) {
_uiState.update {
_uiState.value.copy(
selectedPlace = place
)
}
}
}
data class WhereToGoUiState(
val selectedCategory: Category = Category.NightClub,
val currentPlacesList: List<Place> = emptyList(),
val selectedPlace: Place = LocalDataProvider.getPlaces()[0],
)
The problem is that you recreate the view model every time your screen recomposes.
val viewModel = WhereToGoViewModel()
After you update the state recomposition happens. So your WhereToGoApp
gets, roughly speaking, executed again, it recreates the view model and fetches the state from there. Since the new instance of the view model does not know anything about previous state updates, it simply returns the initial value.