Beacons¶
WaypointBeacon is a small pulse or dot indicator that wraps content to draw attention to it. Beacons run independently from tours and from hint state; if all you need is a flashing dot next to a button, a beacon is the minimum tool.
Core API¶
@Composable
public fun WaypointBeacon(
modifier: Modifier = Modifier,
visible: Boolean = true,
style: BeaconStyle = BeaconStyle.Pulse(),
alignment: Alignment = Alignment.TopEnd,
offset: DpOffset = DpOffset.Zero,
onClick: (() -> Unit)? = null,
content: @Composable () -> Unit,
)
| Parameter | Default | Description |
|---|---|---|
modifier | Modifier | Modifier applied to the outer Box. |
visible | true | Drives AnimatedVisibility, fades in/out. |
style | Pulse() | BeaconStyle.Pulse or BeaconStyle.Dot. |
alignment | TopEnd | Where the beacon sits relative to content. |
offset | Zero | Extra offset after alignment. |
onClick | null | Optional tap handler on the beacon itself. |
content | required | The UI element being decorated. |
Content renders normally underneath; the beacon draws on top, aligned inside the same Box.
Beacon styles¶
BeaconStyle is a sealed interface with two variants.
Pulse¶
Animated expanding ring plus a solid center dot. The ring animates from the dot's radius up to maxPulseRadius over 1500ms on a linear loop, fading from alpha 0.6 to 0 as it expands.
| Parameter | Default | Description |
|---|---|---|
color | Color(0xFFFF4444) | Dot and ring color. |
beaconRadius | 5.dp | Radius of the solid center dot. |
maxPulseRadius | 14.dp | Maximum radius of the expanding ring. |
The rendered canvas size is maxPulseRadius * 2, so the pulse stays inside the allocated space and never clips.
Dot¶
Static filled circle, no animation.
| Parameter | Default | Description |
|---|---|---|
color | Color(0xFFFF4444) | Dot color. |
radius | 5.dp | Dot radius. |
Use Dot when animated pulses are distracting or when you want a cheaper, static indicator for long-lived beacons.
Click handling¶
When onClick is set, the beacon is wrapped in a Modifier.clickable with indication disabled (no ripple). The content underneath keeps its own click handling since the beacon only captures taps on its own canvas area.
var showTip by remember { mutableStateOf(true) }
WaypointBeacon(
visible = showTip,
style = BeaconStyle.Pulse(color = Color.Red),
onClick = {
// Show a popup, navigate, or dismiss.
showTip = false
},
) {
SettingsButton()
}
Full example: new feature beacon¶
@Composable
fun SettingsButton(onClick: () -> Unit) {
var hasSeenFeature by rememberSaveable { mutableStateOf(false) }
WaypointBeacon(
visible = !hasSeenFeature,
style = BeaconStyle.Pulse(
color = MaterialTheme.colorScheme.primary,
beaconRadius = 6.dp,
maxPulseRadius = 18.dp,
),
alignment = Alignment.TopEnd,
offset = DpOffset(x = (-4).dp, y = 4.dp),
) {
IconButton(
onClick = {
hasSeenFeature = true
onClick()
},
) {
Icon(Icons.Default.Settings, contentDescription = "Settings")
}
}
}
The beacon disappears the moment the user taps the button, whether they tapped the button or the beacon itself.
Beacons vs hints¶
A WaypointHint is a beacon plus a tooltip popup plus persistence. Use a standalone WaypointBeacon when you don't need any of that:
| Need | Use |
|---|---|
| Just a flashing dot, no tooltip | WaypointBeacon |
| Beacon with tap-to-show tooltip | WaypointHint |
| Dismissal should persist across sessions | WaypointHint with persistence (plus an optional groupId namespace) |
| Multiple hints, centrally managed | rememberWaypointHintState |
Beacons are also useful inside tooltip content or other components where you only need the visual without any state machine.
See also¶
- Hints, the higher-level component that combines a beacon with a persistent tooltip.