Sirop4ik
Sirop4ik

Reputation: 5233

How to use @Preview with compose function that takes a param?

I have such an implementation:

...

    @Composable
    private fun PhoneVerificationCodeScreen(vm: MyViewModel) {
        Column(...) {
...
            OTPBlock(numberOfCells = NUMBER_OF_OTP_CELLS, isVerifyBtnEnabledState = vm.isVerifyBtnEnabledState)
...
        }
    }

...

There is my preview function:

    class MyViewModelProvider : PreviewParameterProvider<MyViewModel> {
        override val values: Sequence<MyViewModel> = sequenceOf(MyViewModel(
            SavedStateHandle()
        ))
    }

    @Preview(
        name = "Phone-portrait",
        device = Devices.PHONE,
        showBackground = true,
        backgroundColor = 0x111,
        showSystemUi = true
    )
    @Composable
    private fun PhonePreviewVerificationCodeScreen(
        @PreviewParameter(MyViewModelProvider::class) vm: MyViewModel
    ) = PhoneVerificationCodeScreen(vm = vm)

SPOILER: before I included ViewModel as a param it worked as expected.

I went through a few solutions in the google and the last one I tried was by using PreviewParameterProvider, however, it doesn't work as well.

So, the question is - how to "preview" the compose function that takes a param?

ERROR is:

java.lang.ClassNotFoundException: my_package.VerificationCodeViewModelProvider   at java.lang.ClassLoader.loadClass  at java.lang.ClassLoader.loadClass  at java.lang.Class.forName0  at java.lang.Class.forName  at androidx.compose.ui.tooling.ComposableInvoker.invokeComposable 

Upvotes: 0

Views: 1970

Answers (2)

Leonardo Velozo
Leonardo Velozo

Reputation: 648

Probably not the answer you are looking for but I'd extract the viewmodel from the composables, so instead of this:

@Composable
MyComposable(viewModel: MyViewModel) {
    Button(onClick = { 
        viewModel.buttonClicked()
    }) {
        Text(text = viewModel.state.myText)
    }
}

It'd be something like this

val viewModel: YourViewModel = viewModel()
val state: MyState = viewModel.state

@Composable
MyComposable(
    state: MyState,
    onViewEvent: (MyViewEvents) -> Unit,
) {
    Button(onClick = { 
        onViewEvent(MyViewEvents.buttonClicked)
    }) {
        Text(text = state.myText)
    }
}

This also allows you to make any number of previews with different states.

Upvotes: 2

Philio
Philio

Reputation: 4185

The method recommended on the Android developers site for creating previews of composables with ViewModels is to create an inner composable that accepts the ViewModel parameters as arguments:

@Composable
fun MyComposable(viewModel: MyViewModel) {

    val state by viewModel.state.collectAsStateWithLifecycle()

    MyComposable(
        state = state
    ) 
}

@Composable
fun MyComposable(state: MyState) {
   ...
}

Then create a preview for the inner composable:

@Preview
@Composable
fun MyComposablePreview(state: MyState = MyState()) {
    MyComposable(state = state)
}

See: https://developer.android.com/jetpack/compose/tooling/previews#preview-viewmodel

There are various other methods as well, such as using an interface and creating a preview instance of the interface.

Upvotes: 1

Related Questions