Skip to content
View all case studies
Grocery retail technology
Grocery retail technology European retail-technology vendor (confidential) Long-term engagement · multi-country rollout July 2026

European grocery self-scan: whitelabel Flutter on Zebra handhelds for 10+ retail brands

A Flutter monorepo powering a whitelabel self-scan platform — shoppers scan and bag on store-owned Zebra devices, staff run rescan and loss-prevention from a dedicated Attendant app, live across multiple EU grocery chains.

Outcomes

  • One codebase powers 10+ retail brands across NL, UK, DE, and IE
  • Nationwide production rollout for a tier-1 EU grocer (50+ stores)
  • Built-in rescan / loss-prevention workflow with a dedicated staff app
  • Deep Zebra enterprise-hardware integration with offline-tolerant basket
  • Automated multi-brand CI/CD from a single monorepo

Stack

Flutter 3.35 / Dart 3.9Melos monorepoflutter_bloc + BlocMachineChopper REST + OpenAPI Device APIOAuth2 client-credentialsMQTT / Socket.IO / SignalRZebra EMDK / DataWedgeHiveFirebase CrashlyticsGitHub Actions + Fastlane
+

retail brands

+

stores live

EU markets

product apps

The client

A leading European retail-technology vendor whose platform powers in-store self-scan, store-attendant, and terminal-management workflows for some of the continent's largest grocery chains. Shoppers scan and bag items on store-owned Zebra handhelds, then check out without a cashier. Store staff use a separate Attendant app to run rescan sessions, supervise devices, and recover abandoned terminals.

Milannial has contributed long-term Flutter engineering on the platform — the self-scan customer apps, the attendant experience, and the shared packages that keep ten-plus retailer brands on one engine.

This is mission-critical, hardware-bound retail software. When a Sunday-evening rush hits and a scanner won't release from the wall, the team has minutes — not days.

The challenge

Tier-1 grocers want their brand, their workflows, and their hardware — on launch day. Building one app per retailer is a death march. Building one generic app and forcing every chain to live with it is a sales killer.

On top of that, the in-store environment is hostile to normal mobile apps: shared Zebra PS20 / MC17 / MC18 handhelds, cradle and wall docking, DataWedge scan pipelines, spotty store Wi‑Fi, and loss-prevention workflows that must coexist with a frictionless shopper experience.

What we built

01 · Handout

Pick up & go

The device wakes from the screensaver, authenticates on the wall, and releases into the shopper's hand.

02 · Basket

Scan as you shop

Items land in a live basket with a running total — the hybrid processor tolerates aisle dead zones.

03 · Checkout

Finish the trip

The shopper scans the end-of-trip barcode or re-docks; the basket finalizes and payment happens at the station.

04 · Rescan

Loss prevention

Staff pick a flagged trip from the queue, run a partial or full rescan, and review the item diff.

ready for handout

handheld device

basket€24.80
1Organic milk
2Sourdough
3Coffee beans
scan item

handheld device

trip total

€24.80

scan to checkout

handheld device

rescan queue

!Trip #1842
Trip #1847

handheld device

01 · Shop

Scan as you shop

The customer scans items on the handheld; the basket updates live, tolerating network drops via the hybrid processor.

02 · Start

Pick up & go

The device wakes from the screensaver, authenticates, and is handed out to the shopper after cradle or wall release.

03 · Checkout

Finish & pay

The shopper scans the end-of-trip barcode or re-docks; the basket is finalized and payment is handled at the station.

04 · Verify

Rescan tickets

Staff pick a flagged shopper from the queue and run a partial or full rescan, then review the item diff.

05 · Control

Store operations

Force or block rescan, set confirmation mode, and release terminals back to the wall.

06 · Recover

Abandoned devices

The attendant sees scanners left mid-trip via the MQTT feed and releases them back to the wall.

Whitelabel self-scan apps

One Flutter monorepo powers brand-specific customer apps for 10+ grocery retailers across the Netherlands, the United Kingdom, Germany, and Ireland — each with its own theme, app config, and CI pipeline, all sharing the same self-scan engine.

The core shopping trip: device handout → onboarding and terms → live basket → end-of-trip checkout. A hybrid online/offline basket processor queues actions when connectivity drops so a dead aisle doesn't block the trip.

The Attendant app

A separate staff-facing app handles rescan ticket queues, partial and full rescans with item diffs, catalog lookup, store controls (force/block rescan, confirmation modes), and inactive-terminal recovery over MQTT.

One attendant can supervise many self-scan devices — the loss-prevention workflow is built in, not bolted on.

Zebra-native hardware integration

Barcode capture flows through Zebra EMDK / DataWedge native plugins into a shared scanner-engine abstraction. Cradle docking, wall handout events, and terminal-manager integration (device release, app-switching between self-scan and attendant for on-device rescan) are first-class — not afterthoughts.

How it scaled

  1. FOUNDATION · 01 / 07

    Whitelabel engine

    Built a modular Flutter monorepo (apps, modules, packages, plugins) so one self-scan engine can be rebranded per retailer.

  2. HARDWARE · 02 / 07

    Zebra integration

    Bridged Zebra EMDK/DataWedge scanning, cradle docking, and wall/handout events through native plugins.

  3. SELF-SCAN · 03 / 07

    Shopping trip flow

    Delivered handout, onboarding, live basket, and end-of-trip checkout with an offline-tolerant hybrid basket.

  4. LOSS-PREVENTION · 04 / 07

    Attendant & rescan

    Added the staff Attendant app with partial/full rescan, catalog lookup, and store controls.

  5. BACKEND · 05 / 07

    Secure & configurable

    Wired OAuth2 client-credentials auth, runtime store configuration, and MQTT/Socket.IO command buses for unattended terminals.

  6. SCALE · 06 / 07

    Multi-brand rollout

    Automated per-brand CI/CD and shipped production apps across multiple European grocery chains — including a nationwide rollout at 50+ stores for one tier-1 retailer.

  7. NEXT · 07 / 07

    On-device payments

    Prototyped NFC tap-to-pay on the Zebra PS30 for in-app checkout.

Architecture

Store-owned Zebra handhelds run brand-specific Flutter apps built from one shared engine. Apps authenticate via OAuth2 client-credentials (unattended terminals), fetch per-store configuration at startup, and talk to the backend through Chopper REST plus an OpenAPI-generated device API client.

Real-time control uses MQTT topics, a TCP/Socket.IO command server, and terminal-manager integration for cradle/wall events. The Attendant app connects to rescan backends selectable at runtime (legacy plugin vs modern service vs audit), plus scanner-wall card auth and messaging over MQTT / SignalR.

Multi-tenancy is per store via runtime configuration; brand differentiation is compile-time (theme and app config). Deployment is per-brand GitHub Actions + Fastlane → Firebase App Distribution, with secrets in Google Cloud Secret Manager — enterprise MDM distribution, not consumer app stores.

Force-directed architecture graph of the whitelabel self-scan platform: Flutter apps, Zebra hardware, OAuth, MQTT, and multi-brand deployment.

Decisions that mattered

  • Whitelabel monorepo (Melos + pub workspaces, single lockfile) for 10+ brands — one engine, atomic cross-app changes, at the cost of a large shared blast radius that demands strict scope discipline.
  • Runtime store configuration over compile-time flags — same APK behaves per store without rebuilds, at the cost of a heavy config surface and testing matrix.
  • Native Zebra plugin bridge vs off-the-shelf scanner packages — enterprise scan performance and dock events, at the cost of Android-first native maintenance.
  • Rescan backend abstraction chosen at runtime — migrate legacy plugin → modern service without forking the app, at the cost of multiple code paths.
  • Hybrid offline-tolerant basket — retail-floor connectivity gaps must not block shopping, at the cost of sync complexity.
  • OAuth2 client-credentials for unattended shared devices — no interactive login on a wall-mounted scanner, at the cost of per-app secret provisioning.

Outcomes

  • 10+ retail brands on one Flutter codebase across the Netherlands, UK, Germany, and Ireland
  • Nationwide production rollout for a tier-1 EU grocer (50+ stores live)
  • Built-in loss-prevention rescan workflow with a dedicated staff app
  • Deep Zebra enterprise-hardware integration with offline-tolerant basket handling
  • Automated multi-app CI/CD releasing 10+ branded builds from one monorepo

If your business runs on hardware in stores, you need a platform — not an app. This is what one looks like at grocery scale.