Figma parity audit¶
Date: 2026-08-26 · Figma: FANCPIgWZwUJNuM0QPCplp (modified 2026-07-11) · App: apps/mobile @ 4bba76f
An honest screen-by-screen comparison of the shipped Flutter app against the 17-frame Figma design, plus what it would take to close each gap.
Verdict in one line¶
Every designed screen now exists and carries the design's structure; what is still missing is the artwork. All 17 frames have a counterpart — splash and the review screen were built from scratch — and the Material chrome is gone. Final fidelity now waits on illustrations that cannot be extracted from the Figma.
Status — updated 2026-08-26¶
Brand primitives are aligned in both apps, measured from the Figma:
| Before (web / mobile) | Now | |
|---|---|---|
| Brand yellow | #ffe500 / #FBC02D |
#FECC2D |
| Ink | #0b0b0b / #1A1712 |
#000000 |
| Secondary text | #6b6b6b / #6A6252 |
#646465 |
| Borders | #ececec / #EDE8DC |
#EAEAEA |
| Default radius | 0.9rem / 16–18px |
14px |
| Primary CTA | 16px rounded | fully pill |
| Category tints | invented pastels | measured from the design |
The mobile app's warm sand palette (#EDE8DC, #6A6252, #948B78 and friends — 112 usages) appeared nowhere in the design and is now entirely gone: grep returns zero hits.
Chrome pass: complete. mainAppBar was deleted and every screen carries a bespoke header. One raw Material AppBar remains, in planner.dart, because it hosts a TabBar that YamAppBar does not model — an events-first Scale screen with no Figma frame.
core/yam_header.dart—YamHeader,CircleAction,YamPageTitle, andYamAppBar(aPreferredSizeWidgetdrop-in used by 11 screens).core/app_scaffold.dart— rewired toYamHeader, converting 23 screens at once; addsStickyBar.core/brand_art.dart— the artwork swap-in point (see below).
Bottom navigation is the design's floating pill: inset 20px, raised off the edge, radius 20, on a neutral-50 plate with a raised circular AI disc. The active tab is marked with a yellow indicator bar rather than yellow text, because the Figma's own active label is #FECC2D on white (~1.6:1).
Per-screen state¶
| # | Screen | State |
|---|---|---|
| 01 | Splash | Built — did not exist. Yellow ground, lockup, bilingual tagline, glyph scatter, skyline, tap-to-skip |
| 02–04 | Onboarding | Rebuilt — device-frame heroes, service-tile collage, numbered step rail, itemised AI plan card. Copy is now mode-aware: the Figma's super-app deck when super_app_enabled is on, events-first otherwise |
| 05 | OTP | Rebuilt — six discrete code boxes over one transparent field (paste and SMS autofill still work), live 00:45 resend countdown, phone chip with inline edit, security illustration |
| 06 | Login | Rebuilt — phone/Google/Apple moved above the form as designed, drawn Google mark, password eye-toggle, field icons, lockup, corner glow, skyline |
| 07 | Home | Rebuilt — mascot hero, search with AI sparkle + mic + yellow submit, recent-search chips, domains as a horizontal tinted rail, "near you" photo carousel with floating rating badges |
| 08 | Service detail | Rebuilt — 260px full-bleed hero with floating back/favourite, social-proof chip, meta chips, package tiers side by side with escalating icons and a check badge, inclusions rail |
| 09 | Booking details | Updated — bespoke header with a help affordance; time slots became a horizontal card rail matching the date strip |
| 10 | Payment | Rebuilt — branded tile grid, secure badge, sticky total with a lock CTA and an encryption reassurance |
| 11 | Order confirmed | Rebuilt — celebration mascot over confetti, copyable order number, four-stat row, "finding a provider" banner, four-stage tracker |
| 12 | Live tracking | Built — real Google map with the service pin and the provider's live position, refreshed every 20s while en route, plus the en-route band beneath. Backed by a new provider location ping (see below). Needs a mobile-restricted Maps key to switch on — see the gating note |
| 13 | Review | Built — did not exist as a screen. Mascot header, five large stars, tag chips, note with counter, up-to-4 photo upload. Replaced the old bottom sheet, whose sub-ratings were folded in so nothing regressed |
| 14 | My bookings | Updated — large page title with the bell as a circular chip |
| 15 | Messages | Updated — large page title, bell chip |
| 16 | Account | Rebuilt — avatar with camera badge, loyalty tier chip, four-stat row, six colour-coded quick tiles |
| 17 | AI assistant | Rebuilt — mascot header, category tiles that seed the prompt, three "picked for you" cards wired to real catalogue sorts |
Live tracking — how to switch the map on¶
Screen 12 is built end to end, but the Maps SDK needs a mobile-restricted API
key (Android package + SHA-1, iOS bundle id). The project's existing
GOOGLE_MAPS_KEY is IP-restricted to the server for geocoding and cannot be used
from a device, so a new key has to be issued. Until then kMapsEnabled is false,
TrackingMap renders nothing, and the en-route band carries the same facts — no
broken grey rectangle.
To enable:
- Create a mobile-restricted key in Google Cloud (Maps SDK for Android + iOS).
- Android:
flutter build apk -PMAPS_API_KEY=…— the manifest placeholder defaults to empty, so a build without it still succeeds. - iOS: set the
MAPS_API_KEYbuild setting;Info.plistreads$(MAPS_API_KEY)andAppDelegateskipsprovideAPIKeywhen it is empty. - Add
--dart-define=MAPS_ENABLED=true.
The provider's position comes from POST /vendor/orders/{id}/location, accepted
only while the order is on_the_way or in_progress and cleared on completion.
Where the build deliberately differs from the design¶
Beyond the type-scale and radii normalisation below, three controls in the Figma have no backend, so they were not drawn:
- ~~Tip selector~~ — BUILT 2026-08-26.
POST /orders/{order}/tipnow exists: wallet-funded, completed orders only, one tip per order, and the whole amount flows to the vendor's pending settlement with no commission (matchingOrderSettlement::record). A paid-out settlement is never retroactively adjusted. Six tests inOrderTipTest. The screen offers 1/3/5 KWD and disables amounts the wallet cannot cover — the design's 20/50/100 suited a wedding-scale basket, not a 10 KWD cleaning. - Apple Pay and STC Pay tiles (payment) — the API settles cash, KNET and card only. Three tiles are offered, not six.
- Camera and mic in the AI composer, and the mic in the home search field — there is no speech-to-text dependency, so those buttons would only be a second "open search". Adding one is a native plugin with its own permission strings; worth doing deliberately, not as a side effect of a design pass.
Review tag keys were kept as the previous sheet wrote them (friendly, professional, on_time, thorough, great_value) so existing review data keeps aggregating; speed and cleanliness were added for the design.
The three structural blockers¶
1. The app has no artwork. apps/mobile/assets/ contains exactly two files: logo.png and icon.png. The Figma is illustration-led on every screen — 3D-rendered camel mascot in ~8 different poses, 3D category icons, photographic service imagery, decorative Kuwait-skyline and mosque silhouettes, and payment brand marks (Apple Pay, KNET, Visa, Mastercard, STC Pay). None of it exists as an exportable asset; it is baked into flattened frames. No amount of Flutter work closes this — someone has to produce or license the artwork.
2. ~~Chrome is Material, not custom.~~ CLOSED 2026-08-26 — mainAppBar deleted, every screen on a bespoke header, one documented exception (planner.dart).
3. Half the designed flow is switched off in production. Screens 07–13 are the multi-service super-app (browse categories → package tiers → direct booking → dispatch → live map). Production runs events-first with super_app_enabled off, so this flow is unreachable. Rebuilding those seven screens perfects a UI no user can currently open.
Screen-by-screen¶
Effort: S ≈ ½–1 day · M ≈ 1–2 days · L ≈ 3–5 days. "Assets" marks work blocked on artwork that does not exist.
| # | Screen | App file | State | Principal gaps | Effort |
|---|---|---|---|---|---|
| 01 | Splash | — | Missing | No splash screen exists at all. Design: yellow ground, mascot lockup, bilingual tagline, faint service glyphs + skyline | S + assets |
| 02–04 | Onboarding ×3 | onboarding.dart |
Exists, wrong art | Three Material icons (grid_view, bolt, auto_awesome) on invented pastel grounds vs. 3D device mockups, photo collages, mascot, numbered step rail, language switcher |
M + assets |
| 05 | OTP | otp_login.dart |
Exists, close | Has the 6-digit entry and resend timer. Missing: hero illustration, phone-number chip with inline edit, pill CTA, "contact us" footer. Wrapped in an AppBar |
S |
| 06 | Login | login.dart |
Exists, inverted | Social buttons are below the form; design puts phone/Google/Apple first. Material Icons.g_mobiledata/Icons.apple instead of brand marks. No logo lockup, no "مرحبًا بعودتك" headline, no decorative art, no password eye-toggle, no field icons |
M |
| 07 | Home | home.dart |
Exists, generic | Design: mascot hero header, AI-sparkle + mic search, recent-search chips, 5 photographic category cards, dark AI-assistant banner, "near you" carousel, floating pill bottom nav. App: AppBar + plain list |
L + assets |
| 08 | Service detail | service_detail.dart |
Exists, generic | Full-bleed hero photo, "+128 booked this week" social proof, three package tier cards (سريع/شامل/VIP) with icons, inclusions grid, provider card, sticky price+CTA bar | M–L + assets |
| 09 | Booking details | booking_flow.dart |
Exists, generic | Horizontal date strip, time-slot grid, address card with map thumbnail, 0/200 notes counter, priced add-on checkboxes, sticky total bar | M |
| 10 | Payment | payment.dart |
Exists, generic | Six payment tiles with real brand marks, promo-code field, itemised price breakdown, sticky secure-pay bar | M + assets |
| 11 | Order confirmed | order_confirmation.dart |
Exists, generic | Mascot celebration + confetti, copyable order number, 4-stat summary row, "finding a provider" banner, 4-stage tracker | M + assets |
| 12 | Live tracking | order_detail.dart |
Exists, no map | Design is a full-screen map with route, vehicle marker and ETA bubble over a provider sheet with timeline and four actions. Needs Google Maps SDK + a live location channel | L |
| 13 | Review + tip | (inline in booking_detail.dart) |
No dedicated screen | Mascot header, five large stars, quality tag chips, free-text area, up-to-4 photo upload, tip selector (20/50/100 KD) | M + assets |
| 14 | My bookings | bookings.dart |
Exists, generic | Four status tabs, rich cards with service photo, provider avatar, inline progress bar, per-status action buttons | M |
| 15 | Messages | messages.dart |
Exists, generic | Search field, four filter chips, avatar rows with presence dots, unread badges, status chips, mascot empty state | M |
| 16 | Account | account.dart |
Exists, generic | Avatar with camera badge, gold-member chip, 4-stat row, six colour-coded tiles, grouped settings rows | M |
| 17 | AI assistant | ai_assistant.dart |
Exists, generic | Mascot header, large composer with attach/camera/mic, five category tiles, quick-prompt chips, three personalised cards, CTA banner | M + assets |
Rough total: ~28–35 development days for the app screens, and that assumes the artwork already exists. It does not.
Where we deliberately do not match¶
Type sizes. The Figma's home screen sets category titles at 10px and subtitles at 6px; rating badges and nav labels at 8px. These are traced values from scaled-down renders, not design intent, and they sit far below any legible floor — Arabic especially. The token file keeps its 15px body floor (design-tokens.md §2) and the build normalises upward. Matching literally would regress the accessibility work in 3137ef0.
Radii. The Figma carries scaling artefacts (14.23888874, 31.54999923, 11.64999961). We round to a 6/10/14/20/pill scale that preserves the intent.
Typeface. The design uses FF Shamel Family for 66 of its 73 text layers — a commercial Monotype face with no licence in this project. Both apps ship Tajawal + Reem Kufi from Google Fonts. A licence purchase would make this a one-line change.
Yellow as text. The design's active nav label paints #FECC2D text on white — about 1.6:1, failing AA badly. We use ink with a yellow indicator instead.
Recommended sequence¶
- Commission the artwork first. Everything visual is downstream of it. Minimum set: mascot in ~8 poses, five category images, six payment brand marks, three onboarding scenes, two decorative backgrounds. Until this lands, screen work can only be structural.
- Ship the chrome pass — replace
AppBarwith the design's custom headers app-wide, and adopt the floating pill bottom nav. One coherent change, immediately visible, no assets needed. - Do the events-first screens before the super-app ones. Screens 05, 06, 14, 15, 16, 17 are live for users today; 07–13 are behind the flag. Fix what people can actually see.
- Splash + onboarding (01–04) — highest first-impression value per day spent.
- Defer 12 (live tracking) until the Maps key and a realtime channel are in place; it is the only screen with a hard infrastructure dependency.
Caveat on "matching the Figma"¶
Eleven of the seventeen frames are flattened images with zero text layers — service detail, booking, payment, confirmation, tracking, review, bookings, messages, account, AI assistant, and splash. For those, no spacing, size, or colour can be measured; every spec is an eyeball interpretation. Reaching genuine pixel parity on them requires either the layered source or a decision to treat the render as approximate and rebuild to our own token scale. Recommendation: the latter — the tokens now carry the brand faithfully, and the renders are reference, not contract.