Mobile App Development for Grocery Delivery
We build mobile apps for grocery delivery — one of the most technically challenging segments. Imagine: a customer opens the app, sees 5,000 products, adds milk to the cart, and a minute later it’s out of stock. How to avoid this? Only real-time inventory synchronization using WebSocket events. A large catalog (thousands of items), real-time stock management, product substitutions, warehouse order picking, last-mile logistics — each of these blocks is a separate system. Our task is to combine them into a single cohesive whole, ensuring stability and speed.
How to Ensure Catalog Performance with Thousands of Items?
The first problem is catalog performance. 5,000+ SKUs, search, filters by categories, promotions, brands. A naive implementation — load everything and filter locally — fails.
The right approach: server-side pagination and filtering. Flutter + Flutter infinite_scroll_pagination: request 20 items, on scroll to the end — next 20. Search is 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: the main page with promotions and bestsellers updates every 5 minutes, not on every request.
Real-time inventory. An item runs out in the warehouse — it must disappear from the catalog immediately, not by the next sync. Inventory updates via WebSocket or Server-Sent Events (SSE): the client subscribes to a category channel, the server pushes changes.
Example WebSocket connection implementation
final wsUrl = Uri.parse('wss://example.com/socket'); final webSocket = await WebSocket.connect(wsUrl); webSocket.listen((message) { // Process inventory event }); What to Do When an Item Is Out of Stock?
After the order is placed, it goes to the warehouse picker. The picker app is a separate Flutter interface: list of items, barcode scanning for confirmation, marking “out of stock” with substitution suggestion.
Substitutions are a sensitive moment. The picker offers a substitute → the customer receives a push and must confirm or decline. A 5-minute timeout: if no response, an automatic substitution (similar from the same category) is applied or the item is removed from the order with a total recalculation.
This flow requires: WebSocket between the picker app and the customer app, real-time order total recalculation, high-priority push (FCM High Priority, priority: high in payload).
Delivery Slots and Logistics
The customer selects a slot: today 18:00–20:00, tomorrow 10:00–12:00. Available slots are server-side logic: number of active couriers × slot capacity − already booked orders. No hardcoding — dynamic calculation.
Delivery zones: polygons in PostGIS. When entering an address, we check if the point falls within a delivery zone (ST_Contains), and if yes, show available slots for that zone.
Courier tracking — coordinates via WebSocket, marker with animation on flutter_map. Estimated time of arrival via Yandex Routes API taking traffic into account.
Loyalty Program and Personalization
Accumulating points are standard for grocery delivery. But personalization works more effectively: “You usually order milk once a week — it’s probably running out soon.” This is not ML magic, but simple analytics based on order history: purchase frequency × average time between orders = estimated next order date. Push one day before that date.
One-tap cart repopulation: “Repeat last order” — all items in the cart, unavailable ones excluded, the rest with current prices.
Typical Development Mistakes
Implementing the cart only on the client side (SharedPreferences). When logging in from a new device, the cart is lost. The cart should live on the server, synced with the client.
Forgetting about returns. The customer received a spoiled product — there must be a flow in the app: photo + description → return request → decision within 24 hours → refund via YooKassa API.
| Problem | Solution |
|---|---|
| Cart lost on device change | Server-side cart with sync |
| Item out of stock, catalog not updated | WebSocket inventory events |
| Substitution without customer confirmation | Push with timeout and auto-substitution |
| Delivery slots overbooked | Dynamic calculation based on PostGIS |
The cost of developing the client app starts from several million rubles depending on integrations. Support cost after launch is a fixed monthly fee. Up to 40% savings by using Flutter instead of two native teams.
How We Do It: Process and Stack
- Analytics and prototyping (User Flow, architecture)
- Design — responsive for iOS and Android
- Client app development (Flutter)
- Picker app and courier app development
- Web admin panel (Laravel)
- Integrations: 1C, YooKassa, SMS gateways, logistics APIs
- Testing: unit, widget, integration tests, load testing
- Deployment to App Store and Google Play, passing moderation
- Technical documentation, staff training, 2 months of support
Stack
Flutter 3.x + Bloc, Laravel 10 + WebSocket, PostgreSQL + PostGIS + Elasticsearch (for search), Redis, FCM, YooKassa with return support, Yandex MapKit + Yandex Routes API.
| Component | Development Time |
|---|---|
| Client app (catalog, cart, order, tracking) | 16–20 weeks |
| Picker app | 6–8 weeks |
| Courier app | 8–12 weeks |
| Web admin panel | 8–12 weeks |
| Integrations (1C, cash registers, SMS) | 3–6 weeks |
The full development cycle of a grocery delivery app from scratch is 28 to 40 weeks depending on the volume of integrations and scalability requirements. We have 7+ years of experience in mobile development and have delivered over 15 projects in the delivery sector. We guarantee stability and support after launch.
We will evaluate your project for free — contact us for a consultation. Order development and we will prepare a commercial proposal.







