androidandroid-studioandroid-jetpack-compose

How to instantiate viewModel for a screen composable and still be able to preview the composable


What is the preferred way of instantiating a viewModel for a screen composable?

Option 1: inside the composable

eg 1.

    @Composable
    fun LoginScreen(
        navController: NavController,
        modifier: Modifier = Modifier,
        loginViewModel: LoginViewModel = koinViewModel<LoginViewModel>()
    ){
}

eg 2.

@Composable
fun LoginScreen(
    navController: NavController,
    modifier: Modifier = Modifier,
) {
    val loginViewModel = koinViewModel<LoginViewModel>()
}

I note that with this approach, it becomes impossible to show a preview of the screen on Android Studio, unless you create another composable that doesn't care about a viewModel and call it here. I don't want to do that.

Option 2: inside the navgraph, then pass an action lambda and state object

on the navigation graph:

    composable(route = Screen.LoginScreen.route) {
     val loginViewModel = koinViewModel<LoginViewModel>()
     val state by loginViewModel.loginState.collectAsStateWithLifecycle()

     LoginScreen(
       navController = navController,
       state = state,
       onAction = { loginAction ->
       loginViewModel.onAction(loginAction)
      }
     )

  }

on the composable:

@Composable
fun LoginScreen(
    navController: NavController,
    modifier: Modifier = Modifier,
    state: LoginState,
    onAction: (LoginAction) -> Unit
){
}

With this approach, I am able to preview my LoginScreen composable. This however means I need to use actions for all possible UI actions, and pass my state as well. I don't mind this.

Now I am not sure according to best practices, which of these two would be the best approach? Could there be another recommended approach? Any pointers?


Solution

  • Please have a look at the Previews in ViewModels chapter in the official documentation:

    When you try to preview a composable with ViewModel, Android Studio shows an error when rendering the particular composable.

    If you want to preview a composable that uses a ViewModel, you should create another composable with the parameters from ViewModel passed as arguments of the composable. This way, you don't need to preview the composable that uses the ViewModel.

    We can conclude from the documentation that

    Also check out this stackoverflow question which also recommends following approach 2 by lifting the ViewModel to the parent Composable.