Although both tiles and widgets display your content on a remote system surface, building them requires distinct approaches. Migrating your existing tile to a widget means transitioning from rigid layout generation to a dynamic, declarative UI, unlocking new capabilities and a simplified development model.
Choose your implementation strategy
If you're migrating an app that maintains a legacy tile, you must decide how your app provides content to the system. While brand-new widgets should use a single Widget Service, apps with existing tiles must choose between maintaining both services or consolidating on a single Widget Service.
Recommended: Dual service (tile + widget)
Maintaining both a tile and a widget is the recommended path for all apps that have an existing tile. Providing two distinct services provides the best possible user experience across different devices.
- Tile Service: Extend
TileServiceand declare an intent filter forandroidx.wear.tiles.action.BIND_TILE_PROVIDER. - Widget Service: Extend
GlanceWearWidgetServiceand declare an intent filter forandroidx.glance.wear.action.BIND_WIDGET_PROVIDER. - Logical Grouping: Use the
groupattribute in the widget configuration to link the new implementation to your existingTileService. This allows the system to recognize them as a single logical component and automatically migrate the user's existing carousel slot to the new widget on Wear OS 7 or higher.
System Behavior for Dual-Service Setup:
| OS / Device Capability | Resulting Experience |
|---|---|
| Wear OS 3 | Tile is used |
| Wear OS 4, 5, 6 | Tile is used |
| Wear OS 7 (No partial-height support, e.g., Pixel Watch) | Tile is used |
| Wear OS 7 (Partial-height support, e.g., Galaxy Watch) | Widget is used |
Alternative: Single service (widget only)
A single service handles both protocols. While this approach is faster to implement, it relies on a compatibility mode to "adapt" your widget into a tile on devices running lower versions of Wear OS.
If you choose this approach:
- Specify both intent filters: Your service must include intent filters
for both
androidx.wear.tiles.action.BIND_TILE_PROVIDERandandroidx.glance.wear.action.BIND_WIDGET_PROVIDER. This ensures your widget is displayed on tile surfaces in Wear 4, 5, 6, and 7 (where required). - Maintain your existing service name (for seamless upgrades): If you are
replacing an active tile, keeping the same service class name ensures that
users who have your tile in their carousel will see it automatically update
to the new widget. While Wear OS 7 uses the
groupattribute in the widget configuration XML to logically link different components, versions of Wear OS lower than 7 rely on the service name to identify them as the "same" component. If you prefer to use a new service name, your app will still function perfectly; however, users on devices running Wear OS version 6 or lower will need to manually re-add the widget to their carousel.
System Behavior for Single Service Setup:
| OS / Device Capability | Resulting Experience |
|---|---|
| Wear OS 3 | Not supported |
| Wear OS 4, 5, 6 | Widget is displayed as a full screen tile |
| Wear OS 7 (No partial-height support) | Widget is translated to a tile |
| Wear OS 7 (Partial-height support) | Widget is used |
*Requires renderer 1.6+.
UI Translation Tips
When translating your UI from ProtoLayout (Tiles) to Remote Compose (Widgets), the mental model shifts from imperative layout builders to a state-driven, Compose-based architecture where UI updates are handled through recomposition. Keep the following principles in mind:
- Adopt Declarative UI: Replace imperative ProtoLayout builders
(
LayoutElementBuilders) with declarative Remote Compose equivalents, such asRemoteText,RemoteColumn, andRemoteBox. - Focus on the Core Content (
mainSlot): Partial-height widgets (e.g.,SMALLandLARGEcontainer types) provide a focused, glanceable surface. Rather than porting a dense, full-screen Tile layout one-to-one, streamline your design to emphasize primary information within the main content area. - Redesign Edge-Aligned Actions: In the tiles architecture, screen-hugging
components like the
EdgeButtonwere anchored to a dedicatedbottomSlot. Because partial-height widgets integrate directly into a vertically scrolling surface, this fixedbottomSlotparadigm no longer exists. Because edge-aligned buttons often served as highly prominent primary actions, migrating requires a deliberate UI redesign rather than a direct component swap. Evaluate alternative UX strategies for your primary actions:- Inline Actions: Integrate an inline
RemoteButtondirectly within yourmainSlotlayout. - Container Taps: Consolidate the interaction by making the entire
widget container tappable using a
PendingIntentAction. - Content Pivot: Re-evaluate the widget's focus. Without a dedicated action slot, consider surfacing richer glanceable data and relying on a single tap to open the full application rather than isolating specific actions on the widget surface.
- Inline Actions: Integrate an inline
- Migrate Event Handling (Actions vs. Lambdas): Tiles rely on interactions
(like
LoadAction) triggering full service callbacks to refresh the UI. Wear Widgets are client-side driven. Standard Compose lambdas cannot be run remotely; instead, provide serializable Declarative Actions (likeValueChangeorPendingIntentAction). Combine these with declarative state (e.g.,rememberMutableRemoteInt) to support instant UI updates without app round-trips. - Adapt Dimensions and Types: When migrating layout dimensions, prefer
deferred layout resolution using
RemoteDp(e.g.,10.rdp) over the standardDp. This ensures the system renderer correctly calculates pixel values at display time. Similarly, use Remote Compose extension functions (.rcforColor,.rsforString,.rdpforDp) to seamlessly convert standard Kotlin and Remote Compose types. - Review Sample Code: To see comprehensive examples of how to build layouts, apply semantic typography, and manage state in Remote Compose, explore the official sample code available in the Wear OS Samples repository.