TLDR: The question is, why do I see both "Route Composable" and "Home Screen" in logcat when composable doesn't depend on the value being updated in my ViewModel? I expected to only see "Home Screen" since that was the only composable that used the state value from the ViewModel.
I recently came across a situation in a production application where my route composable was being called twice, causing a slight flicker in the UI. After digging into it for a few hours I discovered that the cause for the flicker was from using a hiltViewModel within the route for providing the ViewModel to my compose screen. I dug in further only to find out that any time there was a state change within the ViewModel, the Route's composable was recomposed. I would've thought that only the child composable would've recomposed since it was the only one using the state value?
The full project is here but the basic Compose code looks like this. (I removed as much boilerplate as I could)
@Composable
fun AppNav() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home.route) {
addHome()
}
}
private fun NavGraphBuilder.addHome() {
composable(Screen.Home.route) {
Log.i("Home", "Route Composable")
val homeViewModel = hiltViewModel<HomeViewModel>()
HomeScreen(
homeCount = homeViewModel.homeCount,
updateCount = { homeViewModel.updateCount() },
)
}
}
@Composable
fun HomeScreen(
homeCount: Int,
updateCount: () -> Unit,
) {
Log.i("Home", "Home Screen")
Column(modifier = Modifier.fillMaxSize()) {
Spacer(modifier = Modifier.height(25.dp))
Button(onClick = { updateCount() }) {
Text("Increment count")
}
Spacer(modifier = Modifier.height(25.dp))
Text("Count is: $homeCount")
}
}