Skip to content

Recipe: Pick a value from a bottom sheet

Problem: the user taps something and a modal bottom sheet of options appears; picking one returns that value to the caller.

CircuitX’s BottomSheetOverlay takes a strongly typed model and returns a typed result. show() suspends until the sheet finishes. Requires circuitx-overlays and a ContentWithOverlays above this UI.

One-time setup: ContentWithOverlays

Wrap your navigable content in ContentWithOverlays once at the composition root. Every screen within it can show overlays. If you use shared-element transitions, put SharedElementTransitionLayout just outside ContentWithOverlays; otherwise omit it.

setContent {
  CircuitCompositionLocals(circuit) {
    SharedElementTransitionLayout {            // Optional: only if you use shared elements.
      ContentWithOverlays {
        NavigableCircuitContent(navigator = navigator, navStack = navStack)
      }
    }
  }
}

Show the sheet

Wrap the sheet in a reusable suspend function on OverlayHost:

suspend fun OverlayHost.pickPriority(current: Priority): Priority =
  show(
    BottomSheetOverlay(model = Priority.entries.toList()) { priorities, overlayNavigator ->
      Column {
        priorities.forEach { priority ->
          ListItem(
            headlineContent = { Text(priority.label) },
            trailingContent = { if (priority == current) Icon(Icons.Default.Check, null) },
            modifier = Modifier.clickable { overlayNavigator.finish(priority) },
          )
        }
      }
    }
  )

overlayNavigator.finish(priority) closes the sheet and resumes show() with that value.

Drive it from presenter state so the UI stays declarative — a nullable property says “the picker is open”, and the result comes back as an event:

@Composable
fun TaskEditor(state: TaskState, modifier: Modifier = Modifier) {
  if (state.isPickingPriority) {
    OverlayEffect(state.isPickingPriority) {
      val picked = pickPriority(state.priority)
      state.eventSink(TaskEvent.PriorityPicked(picked))
    }
  }
  // … editor fields, one of which opens the picker via an event …
}

If the sheet hosts a reusable component instead of inline content, render that component with SubCircuit and pass the selected value back through its outerEventSink. See Embed a reusable component.

See also: CircuitX overlays · Ask for confirmation · Return a result