Skip to content

Compose, Views, and headless picker

Customize the Android 0.3.4 Compose picker or build a complete Compose/View hierarchy from public components and headless state.

Updated View as Markdown

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.

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.
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

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:

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

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.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close