Four motions were explored for an order leaving one Active Orders tab for the next, each covering both steps a partner repeats all day: Start preparing (New → Preparing) and Mark ready (Preparing → Ready). Condense and drop is the one in active work; the other three are paused below for reference, not deleted. The studio now opens on Condense and drop regardless of which one you follow a link from.
New → Preparing. The partner taps Start preparing, picks a time, and taps Select Time. Each phone starts right after that tap, with the request in flight and the sheet still up. 207 stays in New. Every motion ends with New 2→1 and Preparing 0→1. The vessel is what Preparing means for the business: a restaurant cooks (a pot), a market collects (a basket).
Preparing → Ready. The partner taps Order is ready. There is no sheet: the button spins while
donePrepare runs, then turns into a check. Every motion ends with Preparing 2→1 and Ready 0→1, in Ready's blue.
The vessel is a hand-off: a takeaway bag that folds shut and gets a seal, or a shopping bag whose handles come together.
A pickup order's button is amber ("Order is ready for pickup"); the rest is identical.
The first half of Condense and catch and the second half of Wrap and drop. No stamp, no fold: the card shows its new state, winds up, and gathers into a solid #206 token in flight. Meanwhile the destination tab opens its vessel, and the token falls into it from above.
donePrepare runs.Compose: Condense and catch's single overlay Animatable for the token (drawn in a Canvas, card content from a rememberGraphicsLayer() snapshot), plus the vessel from Wrap and drop. The only change from Condense and catch is the path's end point and a smaller token. It is the shortest of the vessel motions: no stamp, no fold, no wrap.
Formerly Prep drop, Prep flip, and Pack and hand off. The card folds away on its own fold, the header wraps once to the tab's width, a #206 label goes on, and the parcel is tossed into the tab's vessel.
FoldableCardConfig.Fast, cropping the ink with it.Compose: the fold reuses rememberFoldableCardState. The wrap draws three clipped thirds of one rememberGraphicsLayer() recording, the ends on graphicsLayer { rotationY; transformOrigin; cameraDistance }. The vessel is two vector layers so the slip falls between them.
The card winds up, launches, and gathers into a solid #206 token that comes up under the destination tab, squashes against it, rebounds once, and is drawn in. No vessel.
Compose: one overlay Canvas driven by a single Animatable, draw-only. The impact is two springs on FilterTabItem's graphicsLayer.
Wrap and drop without the vessel: the wrapped parcel lands on the tab itself, squashes, and sinks out of sight as the counts roll.
OrdersFilterBar sits in Modifier.collapsingSection(collapse), driven by
TopAppBarState.heightOffset. After a partner scrolls to an order, the bar is usually hidden, so the tab that
should receive the order is not on screen. Every motion opens it first: heightOffset springs to 0 (NoBouncy,
MediumLow) as the request lands. Today CollapsingTopBarState.expand() snaps, so this needs an animated
expand. It costs one layout pass per frame and no recomposition. Untick "Bar collapsed" to see it already open.
Yes, with what is already on the classpath (Compose Multiplatform 1.11.1). The flip is
graphicsLayer { rotationY = angle; cameraDistance = 12f * density } on FilterTabItem from one
Animatable; the face is derivedStateOf { abs(angle) < 90f }, so it recomposes only at the two swaps.
Compose has no backface culling, and swapping at 90° means it never needs it. The tab bar and the list live in the same
Scaffold in ActiveOrdersScreen, so one overlay above both draws the token or parcel and the vessel's
two layers. New work: a short-lived departing copy of the card (the refetch removes it from paging at once), the animated
expand(), and the four vessel vectors in ui/vectors.
HapticEvent through LocalAppHaptics, so the user's haptics setting is respected. Two Confirms (acknowledged, caught) and at most one SegmentTick.donePrepare, so the partner never feels two for one tap.| Piece | Where it comes from | What it means for motion |
|---|---|---|
| The card | ActiveOrderCard: a header, then FoldableSections for timer, details, finance, and actions. |
The sections already fold with rotationX; FoldableCardConfig.Fast is 126 ms per section with a 49 ms stagger. |
| The tabs | OrdersFilterBar in the TopBar: New (amber), Preparing (jade), Ready (blue), each with a count. |
Counts come from state.statistics. Ready has Waiting pickup / In delivery sub-tabs below it. |
| Start preparing | startPrepare emits OrderStatusChanged; the ViewModel sends OrderLeftList(orderId) when a tab is selected. |
The event that starts the motion already exists. |
| Mark ready | OrderPrimaryAction.MARK_AS_READY (success, "Order is ready") or MARK_AS_READY_FOR_PICKUP (warning). Both call donePrepare under the mutatingOrderId spinner; bags first when onMarkOrderReady needs them. |
Same OrderLeftList trigger. The spinner is the motion's lead-in. |
| Business type | BusinessType in AppSettings (Restaurant, Market). Unknown behaves as Restaurant, matching accountAllowsInlineOrderItems. |
Chooses the vessel: pot or basket for Preparing, takeaway bag or shopping bag for Ready. |
Light-theme Haat colors copied from Palette, for this mock only. Times are from the demo's own clock with the
bar collapsed. On an Android phone, the haptic beats vibrate through navigator.vibrate after you first tap the page.