โš™๏ธ Advanced Android Engineering and Emerging Technology

App Widgets and Shortcuts

Widgets put live information on the home screen, and shortcuts let users jump straight to key actions with a long-press. Both increase engagement without opening the app. Jetpack Glance lets you build widgets with a Compose-style API.

โœ๏ธ Yunok Tech ๐Ÿ“… Updated โฑ 3 min read ๐ŸŽฏ Intermediate
On this page
  1. How a Glance widget works
  2. Build a widget with Glance
  3. Register the widget
  4. Updating the widget
  5. App shortcuts
  6. Best practices
  7. Troubleshooting
  8. Frequently asked questions
  9. Conclusion

Widgets run inside the launcher process, not your app, so they use RemoteViews under the hood. Glance hides that complexity behind composable-style functions, but its components are separate from regular Compose UI, so not every Compose API works in a widget.

How a Glance widget works

Glance widget architecture Your app's data layer feeds a GlanceAppWidget whose provideGlance function builds content. Glance translates it into RemoteViews, which the launcher displays. Updates are triggered by the app, by WorkManager on a schedule, or by user actions in the widget. Data layerrepo, DataStore GlanceAppWidgetprovideGlance() RemoteViewsgenerated Launcher Updates: app calls update() ยท WorkManager schedule ยท user actions
Widgets don't update continuously. You push updates when data changes.

Build a widget with Glance

build.gradle.ktsGradle KTS
dependencies {
    implementation("androidx.glance:glance-appwidget:1.1.1")   // check for the latest stable version
    implementation("androidx.glance:glance-material3:1.1.1")
}
TasksWidget.ktKotlin
class TasksWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        val tasks = TaskRepository.get(context).todayTasks()   // load data (suspend)
        provideContent {
            GlanceTheme {
                Column(
                    modifier = GlanceModifier.fillMaxSize()
                        .background(GlanceTheme.colors.background)
                        .padding(12.dp)
                        .clickable(actionStartActivity<MainActivity>()),
                ) {
                    Text("Today (${tasks.size})", style = TextStyle(fontWeight = FontWeight.Bold))
                    tasks.take(3).forEach { Text("โ€ข ${it.title}", maxLines = 1) }
                }
            }
        }
    }
}

class TasksWidgetReceiver : GlanceAppWidgetReceiver() {
    override val glanceAppWidget: GlanceAppWidget = TasksWidget()
}

Note the imports: androidx.glance.layout.Column and androidx.glance.text.Text, not the Compose UI versions.

Register the widget

AndroidManifest.xml + res/xml/tasks_widget_info.xmlXML
<receiver android:name=".TasksWidgetReceiver" android:exported="true">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
        android:resource="@xml/tasks_widget_info" />
</receiver>

<!-- res/xml/tasks_widget_info.xml -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="180dp" android:minHeight="110dp"
    android:targetCellWidth="3" android:targetCellHeight="2"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/glance_default_loading_layout"
    android:description="@string/tasks_widget_description"
    android:widgetCategory="home_screen" />

Updating the widget

Trigger updatesKotlin
// After data changes in the app
suspend fun refreshWidgets(context: Context) {
    TasksWidget().updateAll(context)
}

// Periodically (minimum 15 min) with WorkManager
class WidgetRefreshWorker(ctx: Context, p: WorkerParameters) : CoroutineWorker(ctx, p) {
    override suspend fun doWork(): Result {
        TasksWidget().updateAll(applicationContext)
        return Result.success()
    }
}

Avoid frequent updatePeriodMillis updates: they wake the device and drain battery. See WorkManager.

App shortcuts

Shortcuts appear on long-press of the launcher icon. Use static shortcuts (XML) for core actions, dynamic ones for context-dependent actions, and pinned ones that the user adds to the home screen.

Dynamic shortcutKotlin
val shortcut = ShortcutInfoCompat.Builder(context, "new_task")
    .setShortLabel("New task")
    .setLongLabel("Create a new task")
    .setIcon(IconCompat.createWithResource(context, R.drawable.ic_add))
    .setIntent(Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com/tasks/new"),
        context, MainActivity::class.java))
    .build()

ShortcutManagerCompat.pushDynamicShortcut(context, shortcut)

Route shortcut intents through the same navigation as deep links.

Best practices

  • Design for multiple sizes, and use SizeMode.Responsive or SizeMode.Exact to adapt layouts.
  • Support dynamic colour with GlanceTheme.
  • Keep widgets glanceable: one purpose, a little key data, and a clear tap target.
  • Provide preview images or generated previews so the widget picker looks good.
  • Keep shortcut counts small (four or so are typically shown).

Troubleshooting

  • Widget shows "Can't load widget": an exception in provideGlance, or an unsupported composable. Check Logcat.
  • Widget not updating: call update/updateAll after data changes, and check that the work actually runs.
  • Clicks do nothing: use Glance actions (actionStartActivity, actionRunCallback), not Compose clickable lambdas.

Frequently asked questions

Can I use regular Compose in a widget?

No. Glance provides its own composables that map to RemoteViews. The programming model is similar, but the components differ.

Can widgets show lists?

Yes. Glance offers LazyColumn for widgets, backed by RemoteViews collections.

Conclusion

Use Glance for widgets with a Compose-like API, push updates when data changes (or with WorkManager), keep widgets glanceable and responsive, and add shortcuts for key actions routed through your navigation.

Next step: build for the wrist with Wear OS App Development.

Official references

๐Ÿ“ฑ Apps built by Yunok Tech

Real Android apps on Google Play, free to download.

See all our apps โ†’

Ready to build real Android applications?

Jetpack Compose clicks once you build real screens with real state. Practise modern Android UI development on projects that go beyond toy examples.

Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.

Explore the Android course and take your next step as an Android developer.

Share: LinkedIn X / Twitter WhatsApp