Skip to content

Lifecycle and recovery

Keep the SeatLayer Flutter 0.7.2 picker current across routes, backgrounding, hold lapse, unavailable seats, and checkout ownership changes.

Updated View as Markdown

Mobile time is discontinuous. A buyer can background the app, open checkout on another route, lock the device, or return after another buyer has purchased the same seats. SeatLayer Flutter 0.7.2 reconciles those moments with server truth instead of trusting an in-app timer that may not have run.

Ready-made picker behavior

The drop-in picker observes application lifecycle automatically. When it returns to the foreground it:

  1. reports foreground lifecycle to SeatLayer;
  2. applies any hold-lapse or lost-inventory state already available;
  3. optionally asks for a fresh availability read; and
  4. reconciles authoritative picker state before accepting another buyer action.

Selection, camera, floor, focused section, and cart presentation remain in place unless authoritative inventory makes a selected object unavailable.

Observe routes as well as app lifecycle

Register the SDK route observer when another Flutter route can cover the picker:

MaterialApp(
  navigatorObservers: [SeatLayerPicker.routeObserver],
  home: const App(),
);

Without it, background/foreground reconciliation still works. The observer adds the route-return half: a picker covered by an in-app checkout or details route can refresh when that route pops.

Control automatic behavior

SeatLayerPicker(
  configuration: configuration,
  options: const SeatLayerPickerOptions(
    refreshOnResume: true,
    announceHoldLapse: true,
  ),
  callbacks: SeatLayerPickerCallbacks(
    onHoldExpired: returnBuyerToSelection,
    onSelectedObjectUnavailable: explainLostInventory,
  ),
  onCheckout: (handoff) async {
    await checkoutBackend.begin(holdId: handoff.holdId);
  },
)

refreshOnResume: false suppresses the SDK’s optional explicit availability request. It does not discard a lifecycle outcome the SDK already reported; ignoring that result could leave the cart showing inventory the server has released.

announceHoldLapse: false disables the built-in visible lapse message while state and callbacks still update. Use it when your host provides an equivalent, accessible explanation.

What happens when a hold lapses

When an owned hold is gone, the picker stops its countdown immediately, clears the hold state, calls onHoldExpired, and announces the outcome once. Seats that remain available can be offered back through the built-in SeatLayerHoldLapseNotice; selecting the recovery action reselects and holds them again.

The offer is not a promise. A seat can sell between the availability read and the buyer’s recovery tap. That race returns a typed failure and leaves the UI in a truthful unheld state.

Unavailable selected objects

A refresh can discover that another buyer purchased a selected seat while this app was away. The picker deselects that object and reports it through onSelectedObjectUnavailable. Do not silently put it back into the cart or calculate payment from the stale local selection.

Custom layout responsibility

SeatLayerPickerScope retains the picker state and lifecycle machinery. A custom component can request a read explicitly:

final picker = SeatLayerPickerScope.controllerOf(context);
final result = await picker.refreshAvailability();

Use the typed result and current SeatLayerPickerState; do not compare local timers. If a completely headless host takes over lifecycle control, it must serialize refresh/synchronize with other inventory-changing actions.

Hold ownership across navigation

Before checkout, a picker-owned hold is released during orderly picker close. After SeatLayerCheckoutHandoff crosses into the host, ownership has moved to your app, so dismissing the picker does not release it.

If onCheckout throws because server validation or navigation failed, the ready picker rejects that exact handoff and returns to a recoverable state. Do not catch the error and pretend checkout opened.

Persist an opaque holdId only when the product promises checkout restoration. On a new picker session, use SeatLayerPickerOptions(initialHoldId: ...); that restored hold is host-owned and picker cart controls do not release it.

Optional picker prewarm

SeatLayerPicker.prewarm() prepares the picker before navigation. It does not contain an event, credential, selection, or hold, so it does not create a hidden buyer session.

@override
void initState() {
  super.initState();
  SeatLayerPicker.prewarm();
}

@override
void dispose() {
  SeatLayerPicker.cancelPrewarm();
  super.dispose();
}

Cancel an unused prewarm when the source screen no longer leads to the picker.

Verification matrix

Scenario Expected outcome
App backgrounds with selection only Selection stays unless refreshed inventory invalidates it
App backgrounds with an open hold Server hold state and expiry replace the paused local timer
Checkout route covers picker Route return triggers reconciliation when the observer is registered
Held seat expires while away Countdown stops, expiry callback fires, buyer receives one recovery explanation
Selected seat sells while away Seat is deselected and reported as unavailable
Host checkout callback throws Handoff is rejected and picker remains recoverable
Host-owned restored hold closes picker Picker does not release the host’s hold

Test these paths on physical iOS and Android devices with a live test event.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close