Home > Enterprise >  ViewModels creation is not supported in Preview Hilt
ViewModels creation is not supported in Preview Hilt

Time:10-05

I have next screen:

@ExperimentalMaterialApi
@Composable
fun AccountListScreen(
    navController: NavController,
    viewModel: AccountListViewModel = hiltViewModel()
) {

And I want to make a preview for this screen.

I'm trying to do this the following way:

@ExperimentalMaterialApi
@Preview(showBackground = true, backgroundColor = 0xFFFFFF, showSystemUi = true)
@Composable
fun AccountListScreenPreview() {
    AccountListScreen(
        navController = rememberNavController(),
        viewModel = hiltViewModel()
    )
}

But unfortunately I get the following error when trying to build a preview:

java.lang.IllegalStateException: ViewModels creation is not supported in Preview

How can this problem be solved? Also here is code of some my classes

CodePudding user response:

What I do in my apps is create a wrapper for my root composable that gets the state from the viewmodel and passes that to a 2nd Composable, where I have my actual content. If there are callbacks that the composable needs to call into the viewModel, then I define those as an interface that the viewModel implements. That way my composable, which I want to preview, receives just a State class and, if needed, an interface for the callbacks, which I can easily stub out.

Something like this:

@Composable
fun CityScreen(
    viewModel: CityViewModel,
    modifier: Modifier = Modifier,
) {
    val state = viewModel.state.collectAsState()
    CityScreen(
        state = state.value,
        callbacks = viewModel,
        modifier = modifier,
    )
}

@Composable
private fun CityScreen(
    state: CityState,
    callbacks: CityCallbacks,
    modifier: Modifier = Modifier,
) {
    // code here
}

CodePudding user response:

Depending on what you wanna test with your preview, you can mock view model injections(like showed in this answer) or the view model itself.

To mock the view model you can create an interface of your view model, in this case it should be abstract class to subclass from ViewModel:

abstract class AccountListViewModel: ViewModel() {
    // interface visible to your view
}

@HiltViewModel
class AccountListViewModelImpl: AccountListViewModel() {
   // implementation which works using dependencies
}

And this is how you can use it:

@Composable
fun AccountListScreen(
    navController: NavController,
    viewModel: AccountListViewModel = hiltViewModel<AccountListViewModelImpl>()
) {

}

@Preview(showBackground = true, backgroundColor = 0xFFFFFF, showSystemUi = true)
@Composable
fun AccountListScreenPreview() {
    AccountListScreen(
        navController = rememberNavController(),
        viewModel = object: AccountListViewModel() {
            // implementation with test data
        }
    )
}
  • Related