Tiles can do more than just display information; they can also be interactive. To make an element such as[`textButton()`](https://developer.android.com/reference/kotlin/androidx/wear/protolayout/material3/MaterialScope#(androidx.wear.protolayout.material3.MaterialScope).textButton(androidx.wear.protolayout.ModifiersBuilders.Clickable,kotlin.Function1,androidx.wear.protolayout.modifiers.LayoutModifier,androidx.wear.protolayout.DimensionBuilders.ContainerDimension,androidx.wear.protolayout.DimensionBuilders.ContainerDimension,androidx.wear.protolayout.ModifiersBuilders.Corner,androidx.wear.protolayout.material3.ButtonColors,androidx.wear.protolayout.material3.TextButtonStyle,androidx.wear.protolayout.ModifiersBuilders.Padding))respond to taps, generate a click handler using[`clickable()`](https://developer.android.com/reference/kotlin/androidx/wear/protolayout/modifiers/package-summary#clickable(androidx.wear.protolayout.ActionBuilders.Action,kotlin.String,kotlin.Float,kotlin.Float))and associate it with the layout element.

You can configure a`Clickable`to trigger an action in two main ways:

1. **Launch an activity directly** : Use[`launchAction()`](https://developer.android.com/reference/androidx/wear/protolayout/ActionBuilders#launchAction(android.content.ComponentName))for cases where you need to open an activity immediately.
2. **Delegate to your tile service** : Use[`loadAction()`](https://developer.android.com/reference/kotlin/androidx/wear/protolayout/modifiers/package-summary#loadAction(androidx.wear.protolayout.expression.DynamicDataMap))to trigger logic within your`TileService`. This is a more flexible approach that lets you refresh the tile's content, update its state, or launch a more complex activity.

| **Note:** The system automatically creates a tappable area of at least 48dp x 48dp around each`Clickable`element for accessibility purposes. If you have multiple interactive elements, maintain enough spacing between them to prevent their touch targets from overlapping.[Material components for tiles](https://developer.android.com/reference/kotlin/androidx/wear/protolayout/material3/package-summary)already follow this guidance.

## Launch an exported activity

If a user tap should immediately launch an activity, use[`launchAction()`](https://developer.android.com/reference/androidx/wear/protolayout/ActionBuilders#launchAction(android.content.ComponentName)). Provide a`ComponentName`to identify the activity. The activity must be[exported](https://developer.android.com/guide/topics/manifest/activity-element#exported). With this approach, you can pass`Intent`extras with the action. However, it's not possible to set custom`Intent`flags.

The following example shows how to create a`Clickable`to launch`TileActivity`with two extras,`name`and`age`:  

```kotlin
textButton(
    labelContent = {
        text("launchAction()".layoutString, typography = BODY_LARGE)
    },
    onClick =
    clickable(
        action =
        launchAction(
            ComponentName(
                "com.example.wear",
                "com.example.wear.snippets.m3.tile.TileActivity",
            ),
            mapOf(
                "name" to ActionBuilders.stringExtra("Bartholomew"),
                "age" to ActionBuilders.intExtra(21),
            ),
        )
    ),
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/wear/src/main/java/com/example/wear/snippets/m3/tile/Interaction.kt#L251-L269
```

Inside the launched activity, retrieve the values from the intent extras:  

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // When this activity is launched from the tile InteractionLaunchAction,
    // "name" will be "Bartholomew" and "age" will be 21
    val name = intent.getStringExtra("name")
    val age = intent.getStringExtra("age")

    // ...
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/wear/src/main/java/com/example/wear/snippets/m3/tile/TileActivity.kt#L47-L58
```

## Handle interactions in your tile service

For more flexible interactions, use[`loadAction()`](https://developer.android.com/reference/kotlin/androidx/wear/protolayout/modifiers/package-summary#loadAction(androidx.wear.protolayout.expression.DynamicDataMap)). When a user taps an element configured with`loadAction`, the system re-invokes your[`TileService.onTileRequest()`](https://developer.android.com/reference/androidx/wear/tiles/TileService#onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest)). This lets you run logic in your service to update the tile, change its state, and perform more complex tasks.

### Refresh the tile's content

The simplest use of`loadAction`is to signal a refresh. Call[`loadAction`](https://developer.android.com/reference/kotlin/androidx/wear/protolayout/modifiers/package-summary#loadAction(androidx.wear.protolayout.expression.DynamicDataMap))with no arguments. When tapped, the system calls`onTileRequest()`, allowing your service to return a new layout with updated content.  

```kotlin
textButton(
    onClick = clickable(loadAction()),
    labelContent = { text("Refresh".layoutString) },
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/wear/src/main/java/com/example/wear/snippets/m3/tile/Interaction.kt#L106-L109
```
| **Note:** To avoid a visual flicker when the tile reloads, design your layout so that only the content that changes is updated. The overall layout structure and animations should remain consistent between reloads.

### Distinguish among multiple interactive elements

If your tile contains multiple interactive elements, you can associate an ID with the`Clickable`modifier:  

```kotlin
textButton(
    labelContent = {
        text("Deep Link me!".layoutString, typography = BODY_LARGE)
    },
    onClick = clickable(id = "foo", action = loadAction()),
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/wear/src/main/java/com/example/wear/snippets/m3/tile/Interaction.kt#L164-L169
```

Inside`onTileRequest()`, you can check this ID using`requestParams.currentState.lastClickableId`to decide what action to perform.

**Example: Launching an activity with a deep link**

This pattern is ideal for launching an activity with a[deep link](https://developer.android.com/develop/ui/compose/navigation#deeplinks). The user tap reloads the tile, your service inspects the ID, and then launches the new activity. To control the back stack, use a`TaskStackBuilder`to provide a better navigation experience for the user. When the user taps the element, they are taken directly to the deep-linked screen (the`message_detail/1`screen from the example). Because[`.addNextIntentWithParentStack()`](https://developer.android.com/reference/android/app/TaskStackBuilder#addNextIntentWithParentStack(android.content.Intent))was used, the parent activity is also added to the back stack. This means if the user swipes back, they will navigate up to the app's main screen (`MessageList`in the example) instead of immediately exiting to the tile. Swiping back a second time returns them to the tile.  

```kotlin
override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile?> {
    val lastClickableId = requestParams.currentState.lastClickableId
    if (lastClickableId == "foo") {
        TaskStackBuilder.create(this)
            .addNextIntentWithParentStack(
                Intent(
                    Intent.ACTION_VIEW,
                    "googleandroidsnippets://app/message_detail/1".toUri(),
                    this,
                    TileActivity::class.java,
                )
            )
            .startActivities()
    }
    // ... User didn't tap a button (either first load or tapped somewhere else)
    // ...
}
https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/wear/src/main/java/com/example/wear/snippets/m3/tile/Interaction.kt#L118-L150
```

Then, in`TileActivity`, configure your navigation to match the`googleandroidsnippets://app/message_detail/{id}`pattern.  

```kotlin
AppScaffold {
    val navController = rememberSwipeDismissableNavController()
    SwipeDismissableNavHost(
        navController = navController,
        startDestination = "message_list",
    ) {
        // ...
        composable(
            route = "message_detail/{id}",
            deepLinks =
            listOf(
                navDeepLink {
                    uriPattern = "googleandroidsnippets://app/message_detail/{id}"
                }
            ),
        ) {
            val id = it.arguments?.getString("id") ?: "0"
            MessageDetails(details = "message $id")
        }
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/wear/src/main/java/com/example/wear/snippets/m3/tile/TileActivity.kt#L65-L101
```

Use`TaskStackBuilder`to provide a better navigation experience for the user. When the user taps the element, they are taken directly to the deep-linked screen---in this example, that's the`message_detail/1`screen. Because[`.addNextIntentWithParentStack()`](https://developer.android.com/develop/ui/compose/navigation#deeplinks)was used, the parent activity is also added to the back stack. This means if the user swipes back, they will navigate up to the app's main screen---`MessageList`in the example---instead of immediately exiting to the tile. Swiping back a second time returns them to the tile.

### Update state within the tile

Your tile has a[`StateBuilders.State`](https://developer.android.com/reference/kotlin/androidx/wear/protolayout/StateBuilders.State)object that stores key-value pairs and persists across reloads. You can use`loadAction()`to update this state when a user interacts with the tile.

To do this, pass a[`DynamicDataMap`](https://developer.android.com/reference/androidx/wear/protolayout/expression/DynamicDataMap)to`loadAction()`containing the new state values.  

```kotlin
textButton(
    labelContent = {
        text("loadAction()".layoutString, typography = BODY_LARGE)
    },
    onClick =
    clickable(
        action =
        loadAction(
            dynamicDataMapOf(
                stringAppDataKey("name") mapTo "Javier",
                intAppDataKey("age") mapTo 37,
            )
        )
    ),
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/wear/src/main/java/com/example/wear/snippets/m3/tile/Interaction.kt#L223-L237
```

When`onTileRequest()`is triggered by this action, you can read the updated data from`requestParams.currentState.stateMap`. This is useful for interactions that directly modify data on the tile, like incrementing a counter or toggling a setting.  

```kotlin
override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile> {

    // When triggered by loadAction(), "name" will be "Javier", and "age" will
    // be 37.
    with(requestParams.currentState.stateMap) {
        val name = this[stringAppDataKey("name")]
        val age = this[intAppDataKey("age")]
    }

    // ...
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/wear/src/main/java/com/example/wear/snippets/m3/tile/Interaction.kt#L178-L203
```

## Recommended for you

- [Create your first Tile in Wear OS](https://developer.android.com/codelabs/wear-tiles)
- [Migrate to ProtoLayout namespaces](https://developer.android.com/training/wearables/tiles/migrate-to-protolayout)
- [Show dynamic updates in tiles](https://developer.android.com/training/wearables/tiles/dynamic)