Skip to main content

Present overlays and transient feedback

Overlay declarations are state-controlled requests. The application owns whether a request exists; the root-scoped host owns its platform window, presentation, and cleanup. Use the overlay tutorial first if you have not installed an Android presenter.

Show a modal bottom sheet​

Keep one stable key while the logical sheet remains visible. A platform dismissal invokes the callback but does not change visible; update the same application state that created the request.

fun UiTreeBuilder.AccountActionsSheet(
visible: Boolean,
onDismissRequest: () -> Unit,
) {
ModalBottomSheet(
visible = visible,
requestKey = "account-actions",
skipPartiallyExpanded = true,
onDismissRequest = onDismissRequest,
) {
Text("Account actions")
}
}

Theme values, nested ProvideModalBottomSheetOverrides, and instance overrides resolve into one complete appearance snapshot. Material 3 supports reversible partial-state policy; the One UI 7 presenter has one intrinsic expanded state. Presenter-specific margins, handles, and gestures stay inside their integration modules.

Anchor a dropdown menu​

Publish an anchor on the rendered node and use the same identifier in the menu request. Logical start/end follows the anchor's layout direction. The default FlipThenClamp policy tries the opposite side near an edge, then keeps the menu inside the visible window.

fun UiTreeBuilder.ProfileMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
) {
Text(
text = "Profile",
modifier = Modifier.overlayAnchor("profile-menu-anchor"),
)
DropdownMenu(
expanded = expanded,
anchorId = "profile-menu-anchor",
requestKey = "profile-menu",
alignment = PopupAlignment.BelowEnd,
onDismissRequest = onDismissRequest,
) {
DropdownMenuItem("Settings", onClick = onDismissRequest)
DropdownMenuItem("Sign out", onClick = onDismissRequest)
}
}

The Android presenter observes global layout and scrolling while the popup is open. Popup content owns its shape and elevation; the PopupWindow transport does not add a second platform shadow. Use Clamp to retain the requested side or None only when exact unclipped coordinates are intentional.

Replace repeated transient feedback​

Snackbar and Toast share one host-owned FIFO lane. Identity is (render session, requestKey), so equal recompositions do not enqueue the same declaration again. Choose the policy explicitly when the same operation can report repeatedly.

fun UiTreeBuilder.SaveResultSnackbar(
visible: Boolean,
onUndo: () -> Unit,
onDismiss: () -> Unit,
) {
Snackbar(
visible = visible,
requestKey = "save-result",
message = "Saved",
actionLabel = "Undo",
queuePolicy = TransientFeedbackQueuePolicy.ReplaceSameKey,
onAction = onUndo,
onDismiss = { onDismiss() },
)
}

Use Enqueue for independent notices, ReplaceCurrent for a new urgent result, ReplaceSameKey for the latest version of one operation, and DropIfBusy for disposable status. Callbacks distinguish timeout, action, gesture, replacement, removal, session cleanup, dropped, and platform dismissal. Android Toast timing is approximate because all supported API levels do not provide a reliable hide callback.

Select and verify the backend​

  • Neutral roots use viewcompose-overlay-android for Dialog, Popup, Toast, nested sessions, and cleanup. Snackbar and modal bottom sheet remain explicitly unsupported.
  • Material roots use viewcompose-overlay-material3-android; setMaterial3UiContent selects its adapter explicitly.
  • One UI roots keep neutral setUiContent and explicitly construct the viewcompose-overlay-oneui7-android host when One UI Snackbar or bottom-dialog presentation is required.

Verify that outside press and Android Back clear caller state, an open menu follows a moving anchor, repeated same-key feedback does not duplicate, and session disposal removes only that session's surfaces. Root selection, transport/session invariants, attribution, and fallback rules belong to ADR-0006. Implementation contracts belong to the neutral, Material 3, and One UI 7 module manuals.