wear-widgets
GitHub用于在 Android 项目中创建、迁移和更新 Wear OS 小组件,支持 GlanceWear 和 Remote Compose 技术栈,涵盖依赖配置、清单注册及 UI 实现。
Trigger Scenarios
Install
npx skills add android/skills --skill wear-widgets -g -y
SKILL.md
Frontmatter
{
"name": "wear-widgets",
"license": "Complete terms in LICENSE.txt",
"metadata": {
"author": "Google LLC",
"keywords": [
"Wear OS",
"Widgets",
"GlanceWearWidget",
"Remote Compose",
"TileService"
],
"last-updated": "2026-10-10"
},
"description": "Create, migrate, update, and verify Wear OS widgets using Glance Wear and Remote Compose in an Android project. Use this skill when implementing GlanceWearWidget, GlanceWearWidgetService, WearWidgetDocument, Remote Compose UI primitives (RemoteColumn, RemoteRow, RemoteBox, RemoteText, RemoteImage, RemoteButton), local state with rememberMutableRemoteInt and valueChange, click actions with pendingIntentAction, widget provider XML containers (SMALL and LARGE), rectangular widget picker PNG previews, or migrating legacy ProtoLayout TileService tiles to Wear OS widgets. Don't use for mobile home screen AppWidgetProvider or GlanceAppWidget implementations, watch face complications, or full-screen Wear OS activities."
}
Wear OS widgets use androidx.glance.wear (GlanceWearWidget and
GlanceWearWidgetService) with androidx.compose.remote (Remote Compose).
They serialize declarative UI and state documents to the watch host process.
Prerequisites and setup
- Resolve library versions : Resolve the latest versions for
{GLANCE_WEAR_VERSION}(androidx.glance.wear),{COMPOSE_REMOTE_VERSION}(androidx.compose.remote), and{WEAR_COMPOSE_REMOTE_VERSION}(androidx.wear.compose.remote). Include-alpha,-beta, or-rcreleases when no stable version exists yet. Don't run./gradlew dependenciesto resolve versions:- Use an available dependency lookup tool if present.
- Otherwise, fetch the latest version from Google Maven metadata (Glance Wear metadata, Compose Remote metadata, Wear Compose Remote metadata).
- SDK and Compose compiler requirements : Ask permission before modifying repo-wide Gradle files unless the user requested widget creation, tile migration, or dependency setup. Set
compileSdk = 37or higher and enablebuildFeatures { compose = true }. Keep the project's existing Android and Kotlin Gradle plugin declarations intact.
dependencies {
implementation("androidx.glance.wear:wear:{GLANCE_WEAR_VERSION}")
implementation("androidx.glance.wear:wear-core:{GLANCE_WEAR_VERSION}")
implementation("androidx.glance.wear:wear-tooling-preview:{GLANCE_WEAR_VERSION}")
implementation("androidx.compose.remote:remote-creation-compose:{COMPOSE_REMOTE_VERSION}")
implementation("androidx.compose.remote:remote-creation:{COMPOSE_REMOTE_VERSION}")
implementation("androidx.compose.remote:remote-core:{COMPOSE_REMOTE_VERSION}")
implementation("androidx.compose.remote:remote-tooling-preview:{COMPOSE_REMOTE_VERSION}")
implementation("androidx.wear.compose.remote:remote-material3:{WEAR_COMPOSE_REMOTE_VERSION}")
}
Codebase exploration
- Search the module for existing
TileService,Material3TileService, orGlanceWearWidgetimplementations,<service>entries inapp/src/main/AndroidManifest.xml, and@drawable/preview assets underapp/src/main/res/. - When migrating an existing tile or modifying an unfamiliar multi-module project, run
./gradlew :app:assembleDebugto verify baseline build health.
Workflow
Step 1: Configure AndroidManifest.xml and provider XML
Register the GlanceWearWidgetService subclass in
app/src/main/AndroidManifest.xml with the BIND_TILE_PROVIDER permission and
BIND_WIDGET_PROVIDER intent action. Attach <meta-data> pointing to an
@xml/ <wearwidget-provider> resource with SMALL and LARGE <container>
entries. When configuring the service, provider XML, or legacy TileService
migration, you MUST follow Widget service and manifest reference.
<service
android:name=".MyWidgetService"
android:exported="true"
android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER"
>
<intent-filter>
<action android:name="androidx.glance.wear.action.BIND_WIDGET_PROVIDER" />
</intent-filter>
<meta-data
android:name="androidx.glance.wear.widget.provider"
android:resource="@xml/my_widget_info" />
</service>
Step 2: Implement GlanceWearWidget and GlanceWearWidgetService
Extend GlanceWearWidget and override provideWidgetData returning
WearWidgetDocument. Annotate the GlanceWearWidgetService subclass with
@AssociateWithGlanceWearWidget for static provider resolution:
// WRONG: Mobile GlanceAppWidget API (don't use on Wear OS)
class BrokenWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {}
}
// CORRECT: Wear OS GlanceWearWidget + GlanceWearWidgetService
@AssociateWithGlanceWearWidget(MyWidget::class)
class MyWidgetService : GlanceWearWidgetService() {
override val widget: GlanceWearWidget = MyWidget()
}
class MyWidget : GlanceWearWidget() {
override suspend fun provideWidgetData(
context: Context,
params: WearWidgetParams,
): WearWidgetData = WearWidgetDocument(
background = WearWidgetBrush.color(Color(0xFF1C1B1F).rc)
) {
MyWidgetContent(params = params)
}
}
Step 3: Build Remote Compose layouts, state, and actions
Remote Compose evaluates UI and state inside the Wear OS host process rather than the app process. When building adaptive layouts or full-bleed backgrounds, you MUST follow Remote Compose layouts reference. When implementing state or click actions, you MUST follow State and actions reference.
- Remote value wrappers : Convert primitives using
.rs(RemoteString),.rc(RemoteColor),.rdp(RemoteDp), and.ri(RemoteInt). Format integers with theRemoteInt.toRemoteStringmember method. - Adaptive container sizing : Branch layout density, typography, and item counts on
params.containerType(ContainerInfo.CONTAINER_TYPE_SMALLversusContainerInfo.CONTAINER_TYPE_LARGE). - Unmasked full-bleed backgrounds : Render background bitmaps inside a root
RemoteBox(RemoteModifier.fillMaxSize) usingRemoteImageandRemoteImageBitmap(bitmap.asImageBitmap). Keep background artwork unmasked (cornerRadius = 0dp) withoutCircleShapeorRoundedCornerShapeclipping. The host clips edges to the watch shape. - Declarative host state and PendingIntent actions : Mutate local widget state with
rememberMutableRemoteIntandvalueChange(state, newValue)instead of in-memory Compose state (mutableStateOformutableIntStateOf). Trigger activity launches or background broadcasts usingpendingIntentAction { ctx -> ... }(always passing a(Context) -> PendingIntentlambda). Refresh active widgets withwidget.triggerUpdateAll(context).
Step 4: Add WearWidgetPreview functions and picker preview PNGs
- Composable border previews (
androidx.glance.wear.tooling.preview) : ImportWearWidgetPreview,SquircleAllWidgetPreviewParams,RoundAllWidgetPreviewParams, andRectangularAllWidgetPreviewParamsfromandroidx.glance.wear.tooling.preview(NEVERandroidx.glance.wear.testing). Define@Previewfunctions invokingWearWidgetPreview(widget, params). - Rectangular picker PNGs (
res/drawable-nodpi/) : UseRectangularAllWidgetPreviewParamsat320 dpior render non-blank, unmasked rectangular PNGs for every<container>in the provider XML:SMALL(CONTAINER_TYPE_SMALL):448x168px (224x84dp at 320 dpi, aspect ratio2.67).LARGE(CONTAINER_TYPE_LARGE):464x288px (232x144dp at 320 dpi, aspect ratio1.61).- Each preview PNG MUST depict the widget's actual UI elements, including backgrounds, text labels, icons, avatar initials, and buttons. NEVER use a blank placeholder, empty unlabeled shapes, or a 1:1 tile preview. When adding composable previews, picker PNGs, or unit tests, you MUST follow Previews and testing reference.
Component mapping
Use this sample table to load complete implementation patterns and imports:
| Capability or API surface | Reference guide |
|---|---|
GlanceWearWidget, GlanceWearWidgetService, AndroidManifest.xml, <wearwidget-provider>, legacy TileService migration |
Widget service and manifest reference |
RemoteBox, RemoteColumn, RemoteRow, RemoteText, RemoteImage, ContainerInfo (SMALL and LARGE adaptive layouts) |
Remote Compose layouts reference |
RemoteButton, rememberMutableRemoteInt, valueChange, pendingIntentAction, BroadcastReceiver, triggerUpdateAll |
State and actions reference |
WearWidgetPreview, SquircleAllWidgetPreviewParams, RoundAllWidgetPreviewParams, RectangularAllWidgetPreviewParams, picker PNGs, captureRawContent tests |
Previews and testing reference |
Legacy ProtoLayout and mobile Glance equivalents
| Legacy or mobile API | Wear OS widget API | Action to take |
|---|---|---|
androidx.wear.tiles.TileService |
GlanceWearWidgetService + GlanceWearWidget |
Replace service and annotate with @AssociateWithGlanceWearWidget. |
GlanceAppWidget / provideGlance |
GlanceWearWidget / provideWidgetData |
Return WearWidgetDocument from provideWidgetData. |
LayoutElementBuilders / primaryLayout |
RemoteColumn, RemoteRow, RemoteBox |
Build layouts with @RemoteComposable functions. |
mutableStateOf / mutableIntStateOf |
rememberMutableRemoteInt + valueChange |
Mutate remote state declaratively using valueChange. |
ModifiersBuilders.Clickable |
RemoteModifier.clickable(pendingIntentAction { ... }) |
Pass a (Context) -> PendingIntent lambda to pendingIntentAction. |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Unresolved reference GlanceWearWidget |
Missing androidx.glance.wear:wear:{GLANCE_WEAR_VERSION} or low compileSdk |
Set compileSdk = 37 and add androidx.glance.wear:wear:{GLANCE_WEAR_VERSION}. |
Unresolved reference toRemoteString |
Imported toRemoteString as top-level extension |
Remove the import; call remoteInt.toRemoteString as a member method. |
Type mismatch on pendingIntentAction |
Passed bare PendingIntent instead of lambda |
Wrap in a lambda: pendingIntentAction { ctx -> pendingIntent }. |
Unresolved wear.tiles or protolayout references after migration |
Other classes or source sets in the module still use legacy tile APIs | Migrate or update the remaining references before removing legacy wear.tiles and protolayout dependencies from build.gradle.kts. |
Verification
- Run
./gradlew :app:assembleDebugto verify that the widget, service,AndroidManifest.xmldeclarations, and@xml/<wearwidget-provider>resources compile and link without errors. - If the project includes unit tests or new widget tests, run
./gradlew :app:testDebugUnitTestand confirm all tests pass. When adding composable previews, picker PNGs, or unit tests, you MUST follow Previews and testing reference.
Antipatterns
- NEVER extend mobile
GlanceAppWidgetorAppWidgetProvider, or use deprecatedGlanceTileServiceorTileServicefor Wear OS widgets. - NEVER use in-memory client Compose state (
mutableStateOf,mutableIntStateOf) or in-process click lambdas for Remote Compose UI. - NEVER pass a bare
PendingIntentinstance topendingIntentAction; always pass a(Context) -> PendingIntentlambda. - NEVER apply
CircleShapeorRoundedCornerShapeclipping to full-bleed widget background artwork. NEVER reuse 1:1 (400x400) tile previews or blank solid-color placeholders for widget picker PNGs.
Best practices
- MUST declare
com.google.android.wearable.permission.BIND_TILE_PROVIDERandandroidx.glance.wear.action.BIND_WIDGET_PROVIDERon<service>. - MUST declare
SMALLandLARGE<container>entries in@xml/with domain-accurate448x168and464x288PNGs inres/drawable-nodpi/. - MUST adapt layout content to
params.containerTypeand include@Previewfunctions for bothSquircleAllWidgetPreviewParamsandRoundAllWidgetPreviewParams.
Version History
- 5bda39f Current 2026-10-19 06:12


