Search code examples
androidandroid-jetpack-composeviewmodeldagger-hilt

Using Android Jetpack Compose, is it a bad practice to define ViewModel for child composable?


Generally speaking, I know that defining more than a single ViewModel per screen is a bad practice. However for special use cases I found it useful.

For instance, let's consider a StatusBar Composable showing the network state. Encapsulating the ViewModel inside the Composable through Hilt injection, makes it reusable in every screen without "polluting" the screen ViewModel with network state related stuffs.

However, this breaks screen previews, because Previews in Android Studio does not works with ViewModels.

So, is that a bad practice? Would it be better to rewrite the logic in every ViewModel of each screen using that composable?

Consider the following code as an example of what I'm referring to.

// ViewModel.kt
@HiltViewModel
class OfflineStatusBarViewModel @Inject constructor(
    networkState: NetworkState,
) : ViewModel() {
    val isOffline = networkState.isAvailable
        .map { !it }
        .stateIn(viewModelScope, WhileUiSubscribed, false)
    
}
// StatusBar.kt
@Composable
fun OfflineStatusBar(
    modifier: Modifier = Modifier,
    viewModel: OfflineStatusBarViewModel = hiltViewModel()
) {
    val isVisible by viewModel.isOffline.collectAsState()
    if (isVisible) {
        OfflineIndicator(modifier = modifier)
    }
}


Solution

  • It is generally considered a bad practice to define a ViewModel for every child composable even with hilt. ViewModels are typically responsible for managing UI-related state and business logic.
    Child composables should be concerned with displaying UI elements, not with managing state or business logic.
    A better approach is to pass the necessary data and callbacks from the parent composable to the child composable. This can be done using state hoisting or by passing the data and callbacks as parameters to the child composable. This will make the composables more reusable and testable, and it will also help to improve performance.
    So Having a ViewModel for every composable can make the codebase more complex and difficult to maintain. It can also lead to duplication of logic and data, as different ViewModels may end up managing similar information.
    Also if you don't config the Hilt correctly, it will be recreated every time the composable is recomposed. This can cause performance issues, especially if the ViewModel is doing a lot of work.

    This is a great article about App Architecture by Google, Be sure to read it.