0

I have a composable that takes 4 parameters

@Composable
fun Authentication(
    owner: LifecycleOwner,
    authViewModel: AuthenticationViewModel,
    context: Context,
    communicationAdapter: CommunicationAdapter
)

I want to be able to see a preview of it so I am learning @PreviewParameter. My preview code is below.

@Preview(showBackground = true)
@Composable
private fun DefaultPreview(@PreviewParameter(PreviewArgProvider::class) args: Quad<ComponentActivity, AuthenticationViewModel, Context, CommunicationAdapter>) {
    Authentication(
        owner = args.first,
        authViewModel = args.second,
        context = args.third,
        communicationAdapter = args.fourth
    )
}

open class PreviewParameterCombiner<T, U, V, W>(
    first: PreviewParameterProvider<T>,
    second: PreviewParameterProvider<U>,
    third: PreviewParameterProvider<V>,
    fourth: PreviewParameterProvider<W>,
) : PreviewParameterProvider<Quad<T, U, V, W>> {

override val values: Sequence<Quad<T, U, V, W>> = sequenceOf(Quad(first.values.first(), second.values.first(), third.values.first(), fourth.values.first()))
}

class PreviewArgProvider(owner: ComponentActivity, viewModel: AuthenticationViewModel) :
PreviewParameterCombiner<ComponentActivity, AuthenticationViewModel, Context, CommunicationAdapter>(
    LifecycleOwnerProvider(owner),
    AuthenticationViewModelProvider(viewModel),
    ContextProvider(),
    CommunicationAdapterProvider(CommunicationAdapter())
)

class CommunicationAdapterProvider(adapter: CommunicationAdapter) : PreviewParameterProvider<CommunicationAdapter> {
 override val values: Sequence<CommunicationAdapter> = sequenceOf(adapter)
}

class ContextProvider @Inject constructor() : PreviewParameterProvider<Context> {

@Inject
@ApplicationContext
lateinit var context: Context

override val values: Sequence<Context> = sequenceOf(context)
}

class AuthenticationViewModelProvider(viewModel: AuthenticationViewModel) : PreviewParameterProvider<AuthenticationViewModel> {
override val values: Sequence<AuthenticationViewModel> = sequenceOf(viewModel)
}

class LifecycleOwnerProvider(owner: ComponentActivity) : PreviewParameterProvider<ComponentActivity> {
override val values: Sequence<ComponentActivity> = sequenceOf(owner)
}

data class Quad<out A, out B, out C, out E>(
val first: A,
val second: B,
val third: C,
val fourth: E
) : Serializable {
override fun toString(): String = "($first, $second, $third $fourth)"
}

I am getting a render error when I try to build

java.lang.NoSuchMethodException: androidx.lifecycle.LifecycleOwner. ()   at java.lang.Class.getConstructor0(Class.java:3349)   at java.lang.Class.newInstance(Class.java:556)   at com.huntergaming.authentication.ui.AuthenticationComposableKt.DefaultPreview(AuthenticationComposable.kt:133)   at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)   at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)   at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)   at java.lang.reflect.Method.invoke(Method.java:566)   at androidx.compose.ui.tooling.CommonPreviewUtils.invokeComposableMethod(CommonPreviewUtils.kt:149)   at androidx.compose.ui.tooling.CommonPreviewUtils.invokeComposableViaReflection$ui_tooling_release(CommonPreviewUtils.kt:188)   at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1$composable$1.invoke(ComposeViewAdapter.kt:571)   at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1$composable$1.invoke(ComposeViewAdapter.kt:569)   at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1.invoke(ComposeViewAdapter.kt:608)   at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1.invoke(ComposeViewAdapter.kt:564)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)   at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)   at androidx.compose.ui.tooling.InspectableKt.Inspectable(Inspectable.kt:64)   at androidx.compose.ui.tooling.ComposeViewAdapter$WrapPreview$1.invoke(ComposeViewAdapter.kt:513)   at androidx.compose.ui.tooling.ComposeViewAdapter$WrapPreview$1.invoke(ComposeViewAdapter.kt:512)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)   at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)   at androidx.compose.ui.tooling.ComposeViewAdapter.WrapPreview(ComposeViewAdapter.kt:508)   at androidx.compose.ui.tooling.ComposeViewAdapter.access$WrapPreview(ComposeViewAdapter.kt:121)   at androidx.compose.ui.tooling.ComposeViewAdapter$init$3.invoke(ComposeViewAdapter.kt:564)   at androidx.compose.ui.tooling.ComposeViewAdapter$init$3.invoke(ComposeViewAdapter.kt:561)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)   at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:384)   at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:228)   at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:227)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)   at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)   at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:150)   at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:114)   at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:113)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)   at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)   at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:106)   at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:162)   at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:161)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)   at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)   at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:161)   at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:144)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)   at androidx.compose.runtime.ComposerKt.invokeComposable(Composer.kt:3332)   at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2577)   at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2566)   at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:540)   at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2566)   at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:2517)   at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:477)   at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:727)   at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:433)   at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:144)   at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:135)   at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:727)   at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:135)   at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:187)   at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)   at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:196)   at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:142)   at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:135)   at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:814)   at android.view.View.dispatchAttachedToWindow(View.java:20479)   at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3489)   at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)   at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)   at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)   at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)   at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)   at android.view.AttachInfo_Accessor.setAttachInfo(AttachInfo_Accessor.java:44)

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Jason Crosby
  • 3,533
  • 4
  • 28
  • 49
  • 1
    It appears from your code that you expect Hilt to work during the preview, but it does not. See this [related answer](https://stackoverflow.com/a/69089891/3585796). Also, your class passed to `PreviewParameter` cannot have constructor arguments: `PreviewArgProvider` should not have `owner` and `viewModel` parameters. It's not quite clear why you pass `context` - you can get it with `LocalContext.current` from any composite. – Phil Dukhov Dec 29 '21 at 04:24
  • @PhilipDukhov so for the `Context` I cant inject it? I need to create a mock for the preview? – Jason Crosby Dec 29 '21 at 11:34
  • As as said, you don't need to inject the context, as it's available in `LocalContext.current`. But you can't inject anything during Preview. If you don't like the mocking idea, you can split your view to smaller views which accept real data instead of view model, so each of those function can be easily previewed. – Phil Dukhov Dec 30 '21 at 02:11

1 Answers1

1

I had a few things wrong with my code. I removed the Inject code for injecting Context but I still had the same error. Turns out it was because I was trying to create an instance of an interface using reflection, which cant be done because there is no constructor.

I no longer had the rendering error but the preview was empty. I notices that I could remove all the code I created for @PreviewParameter and just pass in the values I created in those classes as the value in the preview.

I ended up having to make the parameters in the AuthenticationViewModel nullable so I can pass in null to create an object in preview.

Now everything works but it would be nice if there was a better option other than making parameters nullable. Let me know if there are any other ways of doing this.

Jason Crosby
  • 3,533
  • 4
  • 28
  • 49