More and more of the projects we take on arrive with offline operation written into the requirements. Our working definition is that an app works offline when it can keep operating for extended periods without a connection, and the duration of that period is what drives the architecture, the data model and the cost.

Usually one of three things, and they sit far apart in cost. Showing data downloaded earlier. Accepting one submission and sending it when the signal returns. Or working for hours with no connection at all, which is the definition we use internally.
Most estimates are built for the first case and most expectations are built for the third. So the question we ask is not whether the app needs offline support. It is what the user is doing at the moment the connection disappears, and what they will be doing three hours later.
In field service the answer is concrete. Technicians go into basements and tunnels constantly. Those are places with no signal, and it is normal work rather than an exception, so the business logic has to be designed for those conditions from the start rather than patched afterwards.
The naive version of offline first says put everything on the device. On a navigation product covering Polish inland waterways that approach does not survive contact with reality. There was a lot of map data, and pulling all of it to every device would have been a disaster for the backend.
So the data was split by route segment, and each segment carries a version number. On start the app compares versions and downloads only the segments that changed, then keeps checking for updates on a regular basis. Nothing is fetched because a user asked for a region, and nothing is fetched twice.
The second decision was quieter and saved more. Anything that was certain not to change was kept inside the app itself rather than downloaded. That data leaves the synchronisation problem completely, and in our experience it is the option teams skip, because everything gets treated as something the server has to deliver.
The first launch is deliberately slower, because it pulls most of the data at once. Every launch after that is only synchronisation.
The alternative was a fast first launch and a user who discovers the missing data on the water, which is exactly the moment they cannot download anything. Between a slow start once and a useless app at the wrong time, the choice is not close.
Field testing is where this project turned real. In the simulator everything worked. On the water the app lost its connection and showed the wrong position on the map, and that only surfaced once we physically took it out on a route.
That is the part worth carrying into any offline project. A simulator tests your code. It does not test your conditions, and in offline software the conditions are the product. Signal that fades rather than disappears, a GPS fix that drifts, a device that has been running for hours: none of that exists on a desk.
Liniowiec is a navigation app for Polish inland waterways, built in Flutter with a Nest.js backend. Long stretches of those routes have no cellular coverage, and a navigation tool that stops working there is worse than carrying no tool at all.

Route data, marina information and hazard markers live on the device, so position, bridge clearances and warnings stay available with the connection gone. Positioning matches GPS to kilometer markers on a simplified river diagram, which means the app can place a user relative to the next hazard without asking a server anything.
Users also report hazards, a fallen tree or a bridge with reduced clearance. Those reports are separate entries rather than edits to a shared record, and the administration decides what becomes visible to everyone else. That single choice removed a whole class of conflicts before it could exist.
There are three honest answers, and the right one depends on who owns the truth in the business rather than on what is easiest to build.
There is a fourth option that people forget, and it is often the best one: design the data so the conflict cannot happen. Entries that are appended rather than edited, with visibility controlled centrally, never need a merge rule at all.
Whichever of these you pick, the product needs a way to correct data by hand, through an admin panel or directly from the app. Automatic rules resolve the common cases. The uncommon ones end up wrong, and without an edit path they stay wrong forever.
Everything waiting to be sent is stored locally on the device, so killing the app does not damage the data. That is the baseline. A queue held in memory looks identical in a demo and loses a shift of work the first time the system reclaims memory.
Photos are worth separating. In field products we keep them in their own queue rather than mixed with form data, because a form row and a photo behave nothing alike on a weak connection. We also compress before upload. Sending originals is not a good solution and it raises the running cost of the product, which the client pays every month after launch.
Sessions need the same treatment. If a token expires during a disconnected shift, the app uses a refresh token once the connection returns, rather than signing the person out and putting the queued work at risk.
These are the ones we ask during scoping. Answering them early costs a conversation, and answering them later costs a change request.
When the work happens in places with coverage. An online product is faster to build, easier to reason about and cheaper to run, and the decision can be revisited once there is real usage data instead of assumptions. We ask about it directly during scoping rather than pricing an engine the users will never need.
A middle option covers a lot of products: read offline, queue a small number of critical writes, leave the rest online. That handles the elevator and the parking garage without paying for full synchronisation.
If people genuinely work away from signal, the opposite holds and half measures are worse than either extreme. That case belongs in offline first app development from the first architecture conversation, and it shows up most often in field service and inspection products.
It depends which of the three meanings applies. Reading cached data is a small addition to most builds. Full offline operation changes the data layer, the distribution of data and the interface, so it is scoped as architecture rather than as a feature. The ranges sit on our pricing page.
Offline reading usually can. Offline working is close to a rebuild of the data layer, because the interface has to stop waiting for the network and start reading from a local store. If there is any chance the product needs it, decide before the first version.
sqflite is our proven choice. The library matters less than the shape around it: a local database the interface reads from, a queue of pending changes stored on the device, and synchronisation running in the background rather than in the user's way.
Because it pulls most of the data at once, on purpose. It is a deliberate trade against the alternative, which is a fast first launch and missing data at the moment the user is out of range and cannot download anything.
On our navigation product the data is split by route segment and each segment carries a version number. The app compares versions on start, downloads only what changed, and keeps checking for updates. Data that will never change ships inside the app instead.
The queue stays on the device and the app refreshes the session once a connection is available. Discarding queued work at sign in is how a full shift of work disappears, so that behaviour has to be decided deliberately rather than inherited from a library.

Thomas sets the company's long term strategy and direction, identifies market opportunities, and owns business development and client acquisition. He builds strategic partnerships that last beyond a single project. He works with clients from the first strategy conversation, so their business goals, not just their feature list, drive every decision.
Describe what your users are doing when the signal disappears. You will hear which of the three levels that requires, what it changes in the build, and what we would leave out of a first version.
Book an intro call 30 minutes, no preparation needed.