Mobile App Development for Grocery Delivery
Grocery delivery is one of the most technically complex segments in mobile development. Large catalog (thousands of items), real-time inventory management, item substitutions, order assembly at the warehouse, last-mile logistics. Each of these blocks is a separate system. The task is to make them work as one coherent whole.
Catalog with Thousands of Items — Not Just a List
The first problem encountered when developing grocery delivery is catalog performance. 5000+ SKUs, search, filtering by categories, promotions, brands. A naive implementation — load everything and filter locally — doesn't work.
The right approach: server-side pagination and filtering. Flutter + Flutter infinite_scroll_pagination: request 20 items, on scroll-to-end — next 20. Search — server-side, via PostgreSQL full-text search (tsvector + tsquery) or Elasticsearch for complex queries with typos (fuzzy search via Levenshtein distance).
Caching popular categories in Redis: homepage with promotions and bestsellers updates every 5 minutes, not on every request.
Real-time inventory. Item sold out at warehouse — it should disappear from catalog immediately, not after next sync. Inventory updates via WebSocket event or SSE (Server-Sent Events): client subscribed to category channel, server pushes changes.
Order Assembly and Substitutions
After order placement, it goes to a warehouse picker. Picker app — separate Flutter interface: list of items, barcode scanning for confirmation, marking "out of stock" with substitution offer.
Substitutions are a sensitive moment. Picker suggests replacement → customer gets push and must confirm or decline. 5-minute timeout: if no response — auto-substitution applies (similar item from same category) or item removed from order with price recalculation.
This flow requires: WebSocket between picker app and customer app, on-the-fly order total recalculation, high-priority push (FCM High Priority, priority: high in payload).
Delivery Slots and Logistics
Customer selects slot: today 6-8 PM, tomorrow 10 AM-12 PM. Available slots are server logic: active couriers count × slot capacity − booked orders. Not hardcoded, but dynamic calculation.
Delivery zones: polygons in PostGIS. When entering an address, check if point falls within delivery zone (ST_Contains), if yes — show available slots for that zone.
Courier tracking — coordinates via WebSocket, marker with animation on flutter_map. Estimated arrival time via Yandex Routes API accounting for traffic.
Loyalty Program and Personalization
Accumulating points — standard for grocery delivery. But more effective is personalization: "You usually order milk once a week — you're running out soon". This is not ML magic, it's simple analytics from order history: purchase frequency × average time between orders = predicted next purchase date. Push a day before that date.
Auto-fill cart: "Repeat last order" — one tap, all items in cart, unavailable ones — excluded, others — with current prices.
Common Development Mistakes
Implementing cart only on client (SharedPreferences). When logging in from new device, cart is lost. Cart must live on server, sync with client.
Forgetting about returns. Customer received damaged item — must be flow in app: photo + description → return request → decision within 24 hours → refund via YooKassa API.
Tech Stack
Flutter 3.x + Bloc, Laravel 10 + WebSocket, PostgreSQL + PostGIS + Elasticsearch (for search), Redis, FCM, YooKassa with refund support, Yandex MapKit + Yandex Routes API.
| Component | Development Timeline |
|---|---|
| Customer app (catalog, cart, order, tracking) | 16–20 weeks |
| Picker app | 6–8 weeks |
| Courier app | 8–12 weeks |
| Admin web panel | 8–12 weeks |
| Integrations (1C, registers, SMS) | 3–6 weeks |
Complete development cycle of grocery delivery from scratch — 28 to 40 weeks depending on integration scope and scaling requirements.
Cost is calculated individually after requirements analysis.







