Reputation: 195
I am following the Android Glance documentation (https://developer.android.com/jetpack/compose/glance/create-app-widget) to create a widget. The widget UI is defined in composable function called MyContent
.
I try to preview the UI by adding a composable preview...
@Preview(showBackground = true)
@Composable
fun ContentPreview() {
MyContent()
}
But I get an error stating that it "Failed to instantiate Composition Local."
This preview was unable to find a CompositionLocal. You might need to define it so it can render correctly.
What is the correct way to preview a Glance App Widget?
I am able to build and run the widget on my physical device, but I would like the ability to preview the widget in Android Studio similar to how we can preview other Composable functions
Full StackTrace:
java.lang.IllegalStateException: Invalid applier
at androidx.compose.runtime.ComposablesKt.invalidApplier(Composables.kt:472)
at androidx.glance.layout.ColumnKt.Column-K4GKKTE(Column.kt:102)
at com.example.myproject.MyAppWidgetKt.MyContent(MyAppWidget.kt:46)
at com.example.myproject.MyAppWidgetKt.ContentPreview(MyAppWidget.kt:69)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at androidx.compose.ui.tooling.ComposableInvoker.invokeComposableMethod(ComposableInvoker.kt:163)
at androidx.compose.ui.tooling.ComposableInvoker.invokeComposable(ComposableInvoker.kt:203)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1$composable$1.invoke(ComposeViewAdapter.kt:509)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1$composable$1.invoke(ComposeViewAdapter.kt:507)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1.invoke(ComposeViewAdapter.kt:544)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1.invoke(ComposeViewAdapter.kt:502)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
at androidx.compose.ui.tooling.InspectableKt.Inspectable(Inspectable.kt:61)
at androidx.compose.ui.tooling.ComposeViewAdapter$WrapPreview$1.invoke(ComposeViewAdapter.kt:449)
at androidx.compose.ui.tooling.ComposeViewAdapter$WrapPreview$1.invoke(ComposeViewAdapter.kt:448)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
at androidx.compose.ui.tooling.ComposeViewAdapter.WrapPreview(ComposeViewAdapter.kt:443)
at androidx.compose.ui.tooling.ComposeViewAdapter.access$WrapPreview(ComposeViewAdapter.kt:127)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3.invoke(ComposeViewAdapter.kt:502)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3.invoke(ComposeViewAdapter.kt:499)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:428)
at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:252)
at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:251)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:195)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:119)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:118)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:110)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:158)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:157)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:157)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:142)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:78)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3340)
at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3273)
at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:588)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1013)
at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:520)
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:133)
at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1191)
at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:133)
at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:183)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.kt:314)
at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.kt:192)
at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:140)
at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133)
at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1266)
at android.view.View.dispatchAttachedToWindow(View.java:21291)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3491)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.AttachInfo_Accessor.setAttachInfo(AttachInfo_Accessor.java:54)
at com.android.layoutlib.bridge.impl.RenderSessionImpl.inflate(RenderSessionImpl.java:372)
at com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:450)
at com.android.tools.idea.layoutlib.LayoutLibrary.createSession(LayoutLibrary.java:122)
at com.android.tools.rendering.RenderTask.createRenderSession(RenderTask.java:742)
at com.android.tools.rendering.RenderTask.lambda$inflate$7(RenderTask.java:889)
at com.android.tools.rendering.RenderExecutor$runAsyncActionWithTimeout$3.run(RenderExecutor.kt:202)
at com.android.tools.rendering.RenderExecutor$PriorityRunnable.run(RenderExecutor.kt:316)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
MyAppWidget.kt Full Code
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.Column
import androidx.glance.text.Text
import androidx.glance.Button
import androidx.glance.action.actionStartActivity
import androidx.glance.appwidget.action.actionStartActivity
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.padding
import androidx.glance.text.Text
/* Import Glance Composables
In the event there is a name clash with the Compose classes of the same name,
you may rename the imports per https://kotlinlang.org/docs/packages.html#imports
using the `as` keyword.
*/
class MyAppWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
// Load data needed to render the AppWidget.
// Use `withContext` to switch to another thread for long running
// operations.
provideContent {
// create your AppWidget here
MyContent()
}
}
}
@Composable
private fun MyContent() {
Column(
modifier = GlanceModifier.fillMaxSize(),
verticalAlignment = Alignment.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
Row(horizontalAlignment = Alignment.CenterHorizontally) {
Button(
text = "Home",
onClick = actionStartActivity<MainActivity>()
)
Button(
text = "Work",
onClick = actionStartActivity<MainActivity>()
)
}
}
}
@Preview(showBackground = true)
@Composable
fun ContentPreview() {
MyContent()
}
Upvotes: 4
Views: 2702
Reputation: 81
This feature is fully supported in Android Studio Koala Feature Drop | 2024.1.2
You just need to use glance 1.1.0-rc01 or newer
implementation("androidx.glance:glance:1.1.0-rc01")
implementation("androidx.glance:glance-appwidget:1.1.0-rc01")
implementation("androidx.glance:glance-appwidget-preview:1.0-rc01")
implementation("androidx.glance:glance-preview:1.0-rc01")
And annotate your function with (example of dimensions)
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(widthDp = 200, heightDp = 150)
Then you will see your widget preview when navigating to your GlanceAppWidget class and opening the split screen or design view.
Upvotes: 2