Search code examples
androidkotlinandroid-jetpack-composeandroid-jetpack

Jetpack Compose PreviewParameters Render Error


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)

Solution

  • 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.