An Android app widget needs metadata XML, a Glance UI class, and a registered receiver before it can appear in the home-screen widget picker.
A small home-screen card can surface app data without making the user open the full app, and how to create an Android widget now starts cleanly with Jetpack Glance. For a new Kotlin project, Glance is the modern Android option because it lets you define widget content with Compose-style APIs while Android still renders the result through the app-widget system.
The build has four moving parts: add the Glance dependency, describe the widget in XML, create the Kotlin UI, and register its receiver in AndroidManifest.xml. The example below creates a simple 2-by-1 greeting widget and then shows how to get it into the launcher picker.
What Does An Android Widget Need?
An Android app widget built with Jetpack Glance needs a host app, an AppWidgetProviderInfo XML file, a GlanceAppWidget class, and a GlanceAppWidgetReceiver. Android reads the XML and manifest registration before the launcher can offer the widget to the user.
Glance uses Compose-style Kotlin, but Glance components are not the same as ordinary Jetpack Compose UI components. A normal androidx.compose.material3.Text cannot simply be dropped into widget code; use the matching Glance component instead. If an existing app is a legacy Views or Java project with no Compose setup, Android still supports the classic AppWidgetProvider and RemoteViews approach.
Creating An Android App Widget With Glance
Jetpack Glance turns a Kotlin widget definition into the RemoteViews data Android launchers understand. Android Developers currently lists Glance 1.1.1 as the stable release, so this walkthrough uses that version rather than a release candidate or alpha build.
dependencies {
implementation("androidx.glance:glance-appwidget:1.1.1")
}
android {
buildFeatures {
compose = true
}
}
Create res/xml/greeting_widget_info.xml and define the widget’s starting size, resize behavior, category, and loading layout. Supplying both cell-based sizing and dp-based sizing gives Android 12 and newer launchers a cell target while older launchers still have minimum dimensions to use.
Setting updatePeriodMillis to 0 disables scheduled system refreshes, which is fine for this first static widget. When the widget later shows changing app data, request a Glance update after the data changes or schedule longer background work with WorkManager.
| Project Piece | What It Controls | What To Check |
|---|---|---|
build.gradle.kts |
Glance library access | glance-appwidget is included |
| Compose build feature | Glance compiler support | compose = true |
greeting_widget_info.xml |
Size and picker metadata | Resource is under res/xml |
initialLayout |
Pre-composition loading view | Points to Glance’s loading layout |
targetCellWidth / targetCellHeight |
Default cells on Android 12+ | Both values match the intended footprint |
minWidth / minHeight |
Fallback size on older launchers | Both are present |
GlanceAppWidget |
Widget content | provideGlance() calls provideContent |
GlanceAppWidgetReceiver |
Widget broadcasts | Receiver points to the widget class |
Build The Widget UI In Kotlin
The widget UI lives in a GlanceAppWidget subclass, and its receiver tells Android which Glance widget to compose. Keep the first version small so registration problems are easy to separate from data-loading or interaction bugs.
class GreetingWidget : GlanceAppWidget() {
override suspend fun provideGlance(
context: Context,
id: GlanceId
) {
provideContent {
Text("Hello from Glance")
}
}
}
class GreetingWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = GreetingWidget()
}
Register that receiver inside the element of AndroidManifest.xml. The receiver must accept android.appwidget.action.APPWIDGET_UPDATE, and its metadata resource must point to the XML file created above.
Android Developers documents the same receiver, metadata, sizing, and GlanceAppWidget structure in its Glance app-widget instructions. One detail matters when you expand this sample: Android’s Glance docs give receiver callbacks a 10-second background limit, so longer database or network work belongs in WorkManager before the widget is updated.
How Do You Test The Widget On A Phone?
A finished Android widget should appear in the launcher’s widget picker under the installed app’s name. Install the current debug build first, then add the widget from the home screen rather than trying to launch the receiver directly.
- Run the app on an emulator or Android phone so the package is installed.
- Return to the home screen and touch and hold an empty area.
- Tap Widgets, then find your app in the widget list.
- Open the app’s widget group, touch and hold the greeting widget, and drag it to an empty area.
- Release it on the home screen. The text
Hello from Glanceshould appear inside the widget.
If the app appears in the launcher but no widget is listed, inspect the manifest receiver name and the android:resource path first. Those two references decide whether Android can discover the provider metadata.
Fix The Failures That Stop A Widget Appearing
A widget that does not appear in the picker usually has a registration, metadata, build, or rendering problem. Check discovery failures before changing the Kotlin UI, because a launcher cannot render a widget it never registered.
| Symptom | Likely Cause | First Fix |
|---|---|---|
| No widget under the app name | Receiver missing or class name wrong | Match android:name to the receiver class |
| Provider fails to load | Metadata resource path is wrong | Point android:resource to @xml/greeting_widget_info |
| Build cannot resolve Glance classes | Dependency or Compose support missing | Add glance-appwidget and enable Compose |
| Widget shows only a loading surface | provideGlance() fails before content renders |
Check Logcat for the first composition exception |
| Widget starts at an odd size | Sizing metadata does not match the intended footprint | Set both cell targets and dp minimums |
| Widget never refreshes | updatePeriodMillis is 0 and no update is requested |
Call a Glance update after app data changes |
| Background refresh is unreliable | Long work is running in a receiver | Move long work to WorkManager, then update the widget |
Periodic timing also has limits. Android Developers states that the app-widget system does not honor updatePeriodMillis values below 30 minutes, and current Glance guidance favors infrequent background refreshes to reduce battery work. For data that changes when the user edits the app, an immediate Glance update after the write is usually a better fit than polling.
From Empty Project To Working Widget
A working first widget comes down to six checks, and each one can be verified before adding live data or tap actions. Finish this sequence with the static greeting visible on the home screen, then build on a known-good provider.
- Add stable
androidx.glance:glance-appwidget:1.1.1and make sure Compose support is enabled. - Create the
AppWidgetProviderInfoXML underres/xmlwith both cell and dp sizing. - Use
@layout/glance_default_loading_layoutas the initial loading layout. - Create a
GlanceAppWidgetthat renders one small piece of content. - Create a
GlanceAppWidgetReceiverand register it with the update intent plus metadata inAndroidManifest.xml. - Install the app, open Widgets from the home screen, place the widget, and confirm the greeting renders.
Once that version works, add state, actions, responsive sizing, previews, or scheduled refreshes one feature at a time. That keeps the next failure tied to the last change instead of hiding it inside several new moving parts.
References & Sources
- Android Developers.“Create an app widget with Glance.”Documents Glance widget metadata, receivers, sizing attributes, and Kotlin UI structure.
- Jetpack Glance.“Jetpack Glance.”Official Android overview for building app widgets with Kotlin APIs.
- WorkManager.“WorkManager.”Official API reference for persistent background work used when widget refresh work should not run in a receiver.
