---
title: "Compose, Views, and headless picker"
description: "Customize the Android 0.3.4 Compose picker or build a complete Compose/View hierarchy from public components and headless state."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.seatlayer.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Compose, Views, and headless picker

Android `0.3.4` publishes the complete native picker as two aligned Maven
artifacts. `seatlayer-android` contains the raw API and headless
contract; `seatlayer-android-compose` contains the ready-made adaptive picker,
native components, customization layers, and View/XML host.

```kotlin
implementation("io.seatlayer:seatlayer-android:0.3.4")
implementation("io.seatlayer:seatlayer-android-compose:0.3.4")
```

## Application and SDK ownership

| Native Android UI owns | SeatLayer SDK owns |
| --- | --- |
| Header, price/floor/section navigation, accessibility filters, confirmation, tiers, quantities, cart, hold state, checkout, loading/errors, Test Mode, attribution and Back | Seats, labels, geometry, hit testing, pan/pinch, venue camera, authored 3D, and panorama pixels |
| Compose/View hierarchy, TalkBack, 48dp targets, predictive Back, safe areas, lifecycle and haptics | Authoritative inventory state and supported map capabilities |

The SDK presents one native confirmation, cart, Test Mode indicator, and
checkout action around the venue surface.

## Integration ladder

| Path | API | Host ownership |
| --- | --- | --- |
| Ready Compose | `SeatLayerPicker` | Configuration, callbacks, destination |
| Branded Compose | Theme, strings, options, styles, builders | Brand or selected complete parts |
| Custom Compose | `SeatLayerPickerScope` + public composables | Entire Compose hierarchy |
| Ready View/XML | `SeatLayerPickerView` | Existing View screen and dismissal |
| Custom Views | `SeatLayerPickerStateHolder` + `SeatLayerPickerMapView` | Entire Android View hierarchy |
| Raw map | `SeatLayerView` | Every buyer surface and hold transition |

The first five paths retain the same read-only picker state, typed controller,
ordered actions, supported-feature checks, lifecycle recovery, and checkout
handoff.

## Customize the ready-made picker

Three layers can be mixed without forking the SDK:

1. `SeatLayerPickerOptions`, `SeatLayerPickerTheme`, localized
   `SeatLayerPickerStrings`, and `SeatLayerPickerStyles` change behavior and
   appearance.
2. `SeatLayerPickerBuilders` decorates or replaces any of 25 independent
   complete parts.
3. `SeatLayerPickerScope` and the public components create a host-owned Compose
   hierarchy.

```kotlin
SeatLayerPicker(
    configuration = configuration,
    themeMode = SeatLayerPickerThemeMode.Auto,
    options = SeatLayerPickerOptions(
        layout = SeatLayerPickerLayoutMode.Adaptive,
        haptics = true,
    ),
    strings = SeatLayerPickerStrings.localized("fr-FR"),
    styles = SeatLayerPickerStyles(
        parts = mapOf(
            SeatLayerPickerPart.CheckoutBar to SeatLayerPickerPartStyle(
                cornerRadius = 18.dp,
            ),
        ),
    ),
    builders = SeatLayerPickerBuilders(
        header = { _, defaultContent -> defaultContent() },
    ),
    onCheckout = ::openCheckout,
)
```

Each builder receives `SeatLayerPickerPartContext`: read-only picker state,
native presentation state, typed controller, resolved theme, strings,
options, global styles, and the style for that part. Its second closure argument
renders the default content; calling it decorates the stock component, while
omitting it replaces only that part.

Test Mode and attribution intentionally remain outside the replacement matrix.

## Compose a custom screen

```kotlin
SeatLayerPickerScope(
    configuration = configuration,
    callbacks = SeatLayerPickerCallbacks(
        onCheckout = ::openCheckout,
        onClose = onClose,
    ),
) {
    SeatLayerPickerLifecycle()
    SeatLayerPickerBackHandler()
    SeatLayerPickerHapticEffects()

    Column(Modifier.fillMaxSize()) {
        SeatLayerPickerHeader(onClose)
        SeatLayerPriceLegend()
        Box(Modifier.weight(1f)) {
            SeatLayerPickerMap(Modifier.fillMaxSize())
            SeatLayerPickerMapControls()
            SeatLayerConfirmCard()
        }
        SeatLayerPickerCartSheet()
    }
}
```

The scope owns one picker state. Do not place a second map in another adaptive
branch; the layout moves the existing map between
compact and wide containers so camera, cart, and hold state survive rotation or
window resize.

## View/XML and custom Views

`SeatLayerPickerView` hosts the ready-made tree in a traditional View screen:

```kotlin
binding.picker.bind(
    lifecycleOwner = this,
    configuration = configuration,
    onCheckout = ::openCheckout,
    onReady = ::observeReady,
    onSnapshot = ::observeSnapshot,
    onClose = ::finish,
)
```

For fully custom Views:

1. Create one `SeatLayerPickerStateHolder` for the screen lifecycle.
2. Bind one `SeatLayerPickerMapView` to it.
3. Collect `state`, an immutable `StateFlow` projection.
4. Invoke semantic actions through `stateHolder.controller`.
5. Reproduce lifecycle, Back, Test Mode, attribution, and safe-area duties the
   ready host normally supplies.

## Public component catalogue

| Builder part | Stock public components |
| --- | --- |
| Header and navigation | `SeatLayerPickerHeader`, price legend, floor selector/strip, section navigator, dock bar |
| Map and controls | `SeatLayerPickerMap`, map controls, zoom controls, accessibility filters |
| Decisions | Best-seats form, seat confirmation, tier selector/choices, confirm card, GA/table prompts |
| Cart and holds | Cart list/sheet, countdown, lapse notice, undo notice, checkout bar/button |
| Immersive views | `SeatLayerVenue3D`, `SeatLayerSeatViewChrome` |
| System states | Loading, retryable/fatal error, empty/sold-out/sales-closed, action error |
| Truth components | Test Mode indicator and required attribution |

All stock controls expose TalkBack labels, roles, enabled/disabled state, and
at least 48dp interaction targets. Adaptive compact/wide layout, large text,
RTL, safe drawing, and gesture insets remain SDK responsibilities unless a
custom host replaces the whole hierarchy.

## Headless state guarantees

`SeatLayerPickerStateHolder.state` combines read-only SeatLayer state with
native presentation state. The controller provides ordered
suspending actions for selection, filters, floors/sections, view modes, camera,
GA/tables, holds, best available, lifecycle refresh, checkout, undo, Back, and
close.

- Published state always belongs to the configured Event and supersedes older
  state.
- Malformed optional fields are ignored; invalid schema/session/event identity
  is rejected.
- Empty UI appears only from affirmative sold-out/all-unavailable truth.
- Optional commands are sent only when the exact capability is advertised.
- Repeated checkout taps join one flight.

Custom UI never mutates SDK-owned picker state locally.

## Selection, cart, and checkout

The confirmation surface keeps a seat pending until the buyer accepts it. When
event-authored tiers exist, `confirmPending(tierId)` applies the exact tier
before confirmation, so cart and checkout never briefly carry the wrong price.

GA and variable-table prompts validate quantity against authoritative capacity
and occupancy. Seats, GA, and tables enter one dense cart. Removing a line
opens a session-scoped undo window only while that exact line remains absent.

Checkout returns one `SeatLayerPickerCheckoutHandoff` with opaque `holdId`,
server expiry, currency, priced lines, and display total. Ordinary state omits
`holdId`. Your backend must inspect the hold and calculate payment from server
data.

## Lifecycle, Back, and recovery

The ready picker refreshes availability when supported, reconciles server hold
state, reports lost seats, and presents one recoverable hold-lapse message on
foreground or return from checkout.

Back closes the most local state first: quantity prompt → expanded cart →
panorama → venue 3D → confirmation → focused section → host route. Custom
Compose hosts install the exported lifecycle/Back/haptic effects; custom View
hosts provide equivalent behavior around the same controller.

Activity/process recreation creates a new session. Restore only a host-owned
hold through `initialHoldId` and the same access provider, then let SeatLayer
prove whether it is still active.

## Optional picker prewarm

```kotlin
lifecycleScope.launch {
    val result = SeatLayerPickerPrewarmer.prewarm(applicationContext)
    check(result.engineStarted)
}
```

Prewarm prepares the picker without receiving an Activity, event, credential,
or hold and never starts a buyer session.

## Test Mode and required attribution

Custom hosts must present Test Mode from authoritative Event state and show
bottom-right attribution exactly when the SDK requires it. Server white-label
entitlement is the only authority that disables attribution.

## Production checklist

- Pin both Maven artifacts to `0.3.4`.
- Test every supported Android API level.
- Exercise ready/custom Compose and View paths your product uses.
- Validate TalkBack, 48dp targets, large text, RTL, safe areas, predictive Back,
  rotation, split-screen, process restoration, and picker load failure.
- Test real tiers, GA/tables, lapse recovery, 3D/panorama, rejected checkout,
  Test Mode, and attribution on physical devices.
- Confirm the trusted backend inspects and books the handoff hold.

## Product scope

The picker selects one Event. It does not accept native Performance
Group or Season configuration.

## Related pages

- [Android quick start](/buyer-sdk/android)
- [Raw-map API](/buyer-sdk/android/raw-map)
- [Compare mobile SDKs](/buyer-sdk/mobile)
- [`v0.3.4` source](https://github.com/seatlayer/seatlayer-android/tree/v0.3.4)
- [Tagged Compose entry point](https://github.com/seatlayer/seatlayer-android/blob/v0.3.4/seatlayer-compose/src/main/kotlin/io/seatlayer/android/compose/SeatLayerPicker.kt)
- [Tagged headless controller](https://github.com/seatlayer/seatlayer-android/blob/v0.3.4/seatlayer/src/main/kotlin/io/seatlayer/android/SeatLayerPickerController.kt)
- [Tagged native-picker guide](https://github.com/seatlayer/seatlayer-android/blob/v0.3.4/docs/native-picker.md)
- [Tagged sample](https://github.com/seatlayer/seatlayer-android/tree/v0.3.4/sample)
- [Tagged changelog](https://github.com/seatlayer/seatlayer-android/blob/v0.3.4/CHANGELOG.md)
- [Troubleshooting matrix](/buyer-sdk/mobile#troubleshooting)

Source: https://docs.seatlayer.io/buyer-sdk/android/native-picker/index.mdx
