App widgets in Android with Glance

Glance is a recent addition to the suite of Jetpack libraries. It was introduced to make it faster and easier to build app widgets for surfaces such as the home screen.
Glance is built on top of the Jetpack Compose runtime and requires Compose to be enabled in order to be used. It offers a declarative API, and provides a set of composables to simplify app widget UI development.
This post goes over the basics of building an app widget with Glance. It then explores key features Glance offers, from handling user interactions and errors, managing state, composing the UI, handling resizing and responsiveness to interoperability with RemoteViews
. All code snippets in this post are available in this repo.
Writing Your First App Widget
Building a basic Glance app widget involves 4 steps:
1. Creating the app widget
A Glance app widget is a class that extends GlanceAppWidget
, and must provide -at minimum- the widget’s UI by overriding its Content()
composable function. Glance takes care of calling it whenever it’s necessary.
2. Providing the app widget
AppWidgetProvider
is a convenience class that extends BroadcastReceiver
, it intercepts, parses and correctly dispatches app widget specific broadcast events.
GlanceAppWidgetReceiver
extends AppWidgetProvider
, and provides the extra functionality of generating RemoteViews
from the widget’s composable Content()
function, which is why a GlanceAppWidget
must be attached to a GlanceAppWidgetReceiver
.
3. Registering the widget receiver
The GlanceAppWidgetReceiver
is a BroadcastReceiver
, thus like any other Android component, it must be registered in the app’s manifest.
4. Defining the widget’s metadata
A widget’s metadata defines essential qualities about the widget, such as its initial layout, preview, description, minimum size and resize mode. It’s defined in res/xml
. You can read more about it here.
The above steps result in the following app widget.

Handling user interactions
Glance provides a more intuitive API to handle user interactions, and abstracts away the complexities you’d encounter while using RemoteViews
and PendingIntent
. It provides the following predefined actions:
actionRunCallback
: An action that accepts typed key-value pairs as parameters and executes a callback. The callback extendsActionCallback
and must have a public zero argument constructor, as it’s instantiated at runtime. To pass parameters to an action, define anActionParameters.Key
, specify the type of value it expects, then pass it along with its value toactionParametersOf()
.
Example of actionRunCallback that logs when it's triggered
actionStartActivity
: An action that launches anActivity
. It accepts typed key-value pairs as parameters which can later be retrieved from the launchedActivity
. The action expects theComponentName
,Class
or type of theActivity
to launch.actionStartService
: An action that launches aService
. You can optionally specify whether the service should be launched as a foreground service. The action expects theComponentName
,Class
or type of theService
to launch.actionStartBroadcastReceiver
: An action that launches aBroadcastReceiver
. It expects theComponentName
,Class
or type of theBroadcastReceiver
to launch.
Handling errors
When a GlanceAppWidget
encounters an error, such as when rendering its UI, it displays an error UI. By default, it displays the layout glance_error_layout
when an error occurs. To use a custom error UI, pass in its layout id when extending GlanceAppWidget
.
Composing the UI
Glance provides several composables out of the box that are similar to the ones offered by androidx.compose.ui
, including Box
, Row
, Column
, Text
, Button
, LazyColumn
, Image
and Spacer
. Make sure to use the composables from the package androidx.glance.appwidget
when composing the widget’s UI, otherwise it won’t compile.
Similar to compose’s Modifier
, Glance provides GlanceModifier
which you can use to decorate and/or add behavior to your widget’s composables.
Managing state
A GlanceAppWidget
can store data specific to its UI. To make a widget stateful, provide a GlanceStateDefinition
, which defines how the widget’s underlying data store is created and where the data is stored. Glance provides PreferencesGlanceStateDefinition
, a state definition that stores a widget’s state using DataStore’s Preferences
.
To get the widget’s current state when rendering its UI, use the local composition currentState
, it returns a Preferences
instance when you use a PreferencesGlanceStateDefinition
.You can then use its APIs to query the widget’s state.
To update the widget’s state, use updateAppWidgetState()
, then call GlanceAppWidget.update()
to refresh the widget’s UI.
The gif below shows a counter Glance app widget whose state is composed of a single Int
, the count. When clicked, each of the buttons updates the widget’s state, i.e the count’s value, by calling updateAppWidgetState()
, then also triggers an update to the widget’s UI using GlanceAppWidget.update()
. Check out how this example was implemented here.

Handling responsiveness
Glance makes it easier to handle resizing the app widget by introducing SizeMode
. It provides 3 options that define how a GlanceAppWidget
reacts to being resized.
Single
: The defaultSizeMode
, it results in the widget’sContent()
function being called only once when the widget is initially selected from the widgets selector. It uses the minimum supported size defined as part of the widget’s metadata (inres/xml
). If the available space for the widget changes, i.e the user resizes the widget, the widget’s UI is not refreshed.Exact
: Causes the widget’s UI to be refreshed every time its size changes, i.e every time the user resizes it. This may result in stuttering transitions between sizes if the UI changes, as well as poor performance.Responsive
: Provides a way to define a set of sizes the widget supports. When the widget is selected from the widgets selector, Glance calls itsContent()
function with each of the provided sizes, then stores the corresponding UI in memory. When the widget is rendered for the first time or after being resized, the system selects the right UI to display depending on the widget’s available space. It reuses the UI it had previously stored in memory. This may result in smoother transitions and better performance.
SizeMode
SingleThe gif below shows the impact of the SizeMode
on the actual size of a Glance app widget while it’s being resized. Notice how the size remains the same with Single
, how it constantly changes with Exact
, and how it changes at certain breakpoints (100x100, 250x100, 250x250) with Responsive
. Check out how these widgets were implemented here.

Interop with RemoteViews
Similar to how Jetpack Compose supports interoperability with Android View
s, Glance provides a way to add RemoteViews
to a Glance composition. Simply wrap them in AndroidRemoteViews
. This allows (re)using existing RemoteViews
inside Glance app widgets as you start adopting Glance in your app.
Code sample
This repo contains the code snippets included in this post, as well as other examples of Glance app widgets.
Conclusion
In summary:
- Build an app widget with Glance by defining its UI inside a
GlanceAppWidget
, then make it available through aGlanceAppWidgetReceiver
. - Handle user interactions in your Glance app widget using one of the predefined action handlers
actionRunCallback
,actionStartActivity
,actionStartService
andactionStartBroadcastReceiver
. - Provide a custom error UI for you app widget by passing its layout id when extending
GlanceAppWidget
. - Make use of the composables Glance provides to build your app widget’s UI.
- Make your app widget stateful by providing a
GlanceStateDefinition
. Make use of the predefinedPreferencesGlanceStateDefinition
Glance offers. - Choose how your app widget should handle being resized by setting its
SizeMode
toSingle
,Exact
orResponsive
. - Glance provides interop with
RemoteViews
, so make use of existingRemoveViews
in your Glance app widget.
Want to learn more about Glance and app widgets on Android? Check out: