Skip to content
Mobile Development 4 min read

Building Mobile Applications for a Food Delivery Ecosystem

Three apps, three roles, one order. The interesting problems in a delivery product are not UI problems — they are state, connectivity and idempotency problems.

By

Starter draft, written in general terms. Add specifics from the product you worked on where you are able to.

A food delivery product looks like a consumer app. Structurally it is a small distributed system where three different people act on the same record at the same time, from three different devices, on unreliable networks.

Three roles, one entity#

App Who uses it What they do
Customer The person ordering Browse, order, track, manage profile
Delivery partner The rider Receive assignments, accept or decline, update status
Restaurant / admin The kitchen Manage items and availability, accept and prepare orders

All three are views over the same order. That is the design constraint everything else follows from.

The order lifecycle belongs on the server#

The first instinct is to let each app track status locally and sync. That breaks immediately: a request fails, one app believes the order is preparing, another still shows placed, and the customer is looking at a screen that disagrees with the kitchen.

txt
placed → accepted → preparing → ready → picked_up → delivered
                 ↘ rejected           ↘ cancelled

Rules worth enforcing centrally:

  • Transitions are validated server-side. placed → delivered is rejected, whoever asks.
  • Each transition is permitted for specific roles. A customer cannot mark an order ready.
  • Clients render state; they never decide it.

Once state lives in one place, a whole category of "the apps disagree" bugs stops existing.

Assume every request will be retried#

Mobile networks fail mid-request constantly — a tunnel, a lift, a handover between towers. The client cannot tell "the request never arrived" from "the response never came back", so it retries. Without protection, the retry places a second order.

The fix is idempotency: the client generates a key per logical operation and sends it with the request; the server stores the result against that key and returns the same result for a repeat.

http
POST /api/v1/orders
Idempotency-Key: 7f3a9c2e-14b8-4c6d-9a11-3f7b2e5d8c40

This one property removes more real-world bugs than any amount of client-side retry logic.

Keeping three apps current#

Polling every few seconds drains batteries and hammers the API. What works better:

  • Push notifications for meaningful state changes — an assignment offered, an order accepted.
  • Refresh on focus, so returning to the app shows current state.
  • A live channel only where it earns its keep — an active delivery in progress, not the whole app.

Push is a signal, not a delivery guarantee. Treat a notification as "something changed, go and look", never as the state itself.

What is worth sharing between apps#

Three Flutter apps invite consolidation, and some of it pays off:

  • Worth sharing: the API client, models, authentication handling, design tokens and shared widgets.
  • Usually not worth sharing: navigation and screens. The three roles want genuinely different flows, and forcing them through one structure produces conditionals everywhere.

Location, handled honestly#

Delivery tracking needs location; continuous high-accuracy tracking destroys battery life. Sample at a rate the use case actually needs, stop when a delivery completes, and request permissions with an explanation the user can act on. The platforms are strict here, and they are right to be.

Offline is a spectrum#

Full offline support is expensive. Most of the value comes from a few cheap behaviours:

  • Cache the last-known state so screens open with content rather than a spinner.
  • Show explicitly when data is stale, rather than pretending it is current.
  • Queue write operations and replay them — safely, because the writes are idempotent.

What I would carry forward#

  1. Model the shared entity's lifecycle first, before any screens.
  2. Put transitions and permissions on the server, once.
  3. Make every write idempotent from day one; retrofitting it is painful.
  4. Share the API layer between apps, not the navigation.
  5. Design for a flaky connection as the normal case, not the exception.
Share this article LinkedIn X

Keep reading

Related articles

All articles
Career / Developer Experience 3 min read

What I Learned Working Across Multiple Technology Stacks

Seven years across .NET, PHP, React, Angular and Flutter. The frameworks differ far less than they appear to — and what transfers is more valuable tha...

#Career #.NET #Laravel
.NET 4 min read

React + .NET Web API: A Practical Architecture

How I structure a React front end against a .NET Web API — project layout, the API contract, authentication and the boundaries that keep both sides in...

#React #.NET #Web API
↑ ↓ to navigate · ↵ to open · esc to close Full search