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