One codebase, both stores — and an honest account of where that stops working.
The first version of a cross-platform app is easy, and every demo looks the same. The cost arrives later: the capability with no maintained plugin, the background rule each OS enforces differently, the store review that objects to something the framework made convenient, and the second year when nobody upgraded. We build for that year from the first week.
FACT 01A foreign ministry’s app, built by us in Flutter, is live in production in both public stores — past government review, a national brand, and Apple’s and Google’s own processes.
FACT 02Where no maintained plugin exists we write the native side ourselves: camera, GPS and Bluetooth modules in Swift and Kotlin, behind a Flutter app.
FACT 03This page names the conditions under which we would tell you to go native. Every competitor claims cross-platform solves everything; the useful thing is knowing where it does not.
30 minutes, working session. You leave with a one-page read of your app either way. Nobody follows up more than once.
Home · travel codeSymptom reportingRestriction mapPaired wristband
A national epidemic control platform — the travel-health app of a public-health programme in the Caribbean, live in both public stores. Case →
BUILD 02 / CONDITIONSAS FOUND
The second year of a cross-platform app
None of these mean the framework was the wrong choice. They are what happens when nobody owned the parts that are not the UI.
RELEASE STALLED
Nothing has shipped in five months
A rejection nobody had time to resolve, then a framework upgrade nobody scheduled, then a certificate that expired. The release stopped being a routine and became a project.
OFFLINE
It works until the signal goes
On a train, in a lift, in a basement. The app hangs on a spinner or quietly loses what the user typed, because offline was a state nobody designed.
DEPENDENCY
The plugin is abandoned
The one that does the thing your product depends on. Last commit two years ago, an open issue matching your crash exactly, and a maintainer who has moved on.
BLIND SPOT
The crash rate is unknown
No monitoring, or monitoring nobody reads. The team hears about problems through store reviews — the slowest and most public channel available.
OS RELEASE
September broke something
A new OS, a changed background rule or a tightened permission, and a behaviour that worked for two years stops. Nobody was testing against the beta.
DIVERGENCE
Two codebases appeared anyway
Enough platform branching that the one codebase is now two, interleaved in the same files, with nobody able to say which platform a given change affects.
Live occupancyA React Native app people only trust if what it shows matches what the sensors actually see, which makes the sync path the product rather than a detail. Smart-workspace case →
BUILD 03 / PROOFIN THE STORES
Four apps that had to survive somebody else’s review
The Qatar Ministry of Foreign Affairs’ official app, built in Flutter and shipped on iOS and Android. The hard part is the gates: a government review process, a national brand, and Apple’s and Google’s own review — all before a single user opens it. Cross-platform work rarely has to pass three reviewers who each have a different definition of wrong.
A Flutter app that turns a photo taken at an event into an enriched contact, with AI follow-ups, CRM export and an offline mode. Camera, GPS and Bluetooth are ours: written in Swift and Kotlin and maintained by us rather than depended on from a stranger’s repository. Venue Wi-Fi is usually the first thing to fail, so the capture path has to behave when it does.
Desk, room and locker booking over live IoT data, with a chatbot and a survey engine. The hard part is trust: the moment the app shows a desk as free that somebody is sitting at, nobody opens it again. That makes the sync path the product, not a detail.
A social-scheduling platform architected and led from zero to mass adoption, then expanded into native mobile apps, an AR experience, Amazon TV and Alexa, and Apple Watch. The hard part is surface sprawl: five interaction models over one set of business rules that must not fork.
Tell us what the app must do offline, what hardware it must touch and which stores it must pass.
Weekly installable builds
A build on a real phone, every week
Testing a live build during a client visit in New York. An app that is only ever run in a simulator finds its problems at store review; one that ships to TestFlight and Play internal testing every week finds them while they are still cheap. More about how we work →
BUILD 04 / QUEUEINTERACTIVE
Take the network away and watch what the app admits to
Offline is a design decision, not a library. Switch the network off, keep using the app, then switch it back on. Everything queues honestly — and one of these actions will come back to a world that changed while you were gone.
SCHEMATIC — A BOOKING APP’S PENDING MUTATIONSNETWORK: ONLINE
0 pending
Everything is synced. This is the only state most demos ever show.
Last-write-wins would have resolved that conflict silently, and one of two people would have arrived to a desk somebody else was sitting at. Queued mutations that survive a cold start, a sync path with real conflict resolution, and a UI that says pending rather than pretending — that is the difference between an app that works on a train and one that only works in the office.
BUILD 05 / BOUNDARYWHERE WE SAY NO
Where cross-platform stops paying
Naming this is the whole point. If a requirement below is yours, the honest answer is native for that piece — and going native for one screen is almost always the answer before going native for the app.
Sustained device load
Continuous camera, sensor or GPU work for minutes at a time. The framework is not the problem; the thermal and battery budget is, and native gives you the controls to manage it.
Deep platform integration
A capability with no maintained bridge and a moving native API underneath it — widgets, complications, CarPlay or Android Auto, some background execution. We will write a channel for one capability; we will not write and maintain twenty.
Latency in the UI itself
Interfaces where a dropped frame is the product: heavy real-time drawing, camera overlays that must track without lag. Reachable cross-platform, but the effort curve turns steeply upward.
The team you actually have
If your engineers are native and will stay native, a cross-platform codebase becomes a codebase nobody wants to own. Framework choice is a hiring decision as much as a technical one.
One screen, not the app
The usual right answer. Keep the ninety per cent that is forms, lists and navigation in one codebase, and write the one demanding surface natively behind a channel.
Past three reviewers
A public institution’s app on a public store
The Ministry’s application, built in Flutter and live on both platforms. Right-to-left layout, accessibility that is checked rather than assumed, and a release that had to satisfy a government process before it ever reached Apple or Google. Qatar MoFA case →
BUILD 06 / SCOPETYPICAL RANGES
The work, as we actually sell it
Timelines are indicative ranges and depend on scope.
The framework decision, in writing
1–2 WEEKS
Flutter, React Native or native, argued from your requirements — offline behaviour, hardware access, team skills, three-year maintenance cost — with the conditions that would change the answer. You keep the document either way.
An app built to store-ready
10–20 WEEKS
Architecture signed off before code, navigation and auth first, weekly installable builds from the second week so nobody is judging progress from a screenshot.
Takeover of an app that stopped shipping
4–10 WEEKS
A written state-of-the-app read first: dependency and framework debt, what store review will object to, what the crash data says. Then stabilise, then release.
Offline-first rework
4–10 WEEKS
Local storage, queued mutations that survive a cold start, sync with real conflict resolution, and UI states that are honest about what is pending.
Release pipeline and store operations
2–5 WEEKS
CI to TestFlight and Play internal testing, signing and provisioning, staged rollout, crash and performance monitoring wired in before launch, and a forced-update strategy decided in advance.
Platform channels and native modules
SCOPE-DEPENDENT
When the plugin does not exist or has been abandoned, we write the native side ourselves — as we do for camera, GPS and Bluetooth in Swift and Kotlin — and maintain it rather than depending on a stranger’s repository.
The honest exit
SAID EARLY
If your requirements have outgrown cross-platform, we say so and scope the native work — including the option of going native for one screen rather than the whole app.
Framework upgrades, staged
SCOPE-DEPENDENT
Two majors behind is the normal state of a shipped app. We read what upgrading costs and what breaks — including what moving to React Native’s new architecture would mean for your codebase — then stage it across releases rather than attempting it in one.
BUILD 07 / OBJECTIONSANSWERED PLAINLY
What you are probably thinking
“Cross-platform apps feel cheap.”
Some do. The ones that feel cheap fought the platform instead of using it: wrong navigation gestures, wrong keyboard behaviour, wrong scroll physics, a splash screen covering a slow cold start. Those are decisions, not framework limits — and a ministry’s app built this way is live in both stores.
“Flutter or React Native?”
It depends on two things, and we argue it in writing before you commit. Flutter usually wins on custom, animation-heavy interfaces; React Native wins when the product shares substantial logic and people with an existing React web app. If a requirement makes cross-platform the expensive option, we say so and go native for that piece.
“We’d have to go native eventually anyway.”
Sometimes — and this page names the conditions rather than pretending otherwise: sustained heavy device use, deep platform integration with no maintained bridge, or a team that has become native-only. Going native for one screen is usually the answer before going native for the app.
“Our app is two years behind on its framework.”
That is the most common state of a shipped cross-platform app. It starts with a written read of what upgrading costs and what breaks, then the upgrade is staged across releases rather than attempted in one.
“Can you publish under our developer accounts?”
Yes, and we prefer it. Apps, certificates and store listings belong in your accounts from day one. We operate them; you own them — which matters on the day you want to run the release yourself.
“We tried an outside dev shop and it went badly.”
Usually the same three causes: no senior person accountable, a demo-grade codebase, and a handover that never happened. Here you get direct access to the engineers doing the work, no account-manager layer, a senior architect signing off every project, and tests and documentation as part of the deliverable.
Many of our clients have been with us for 7+ years straight. The Upwork and Clutch records are independently verifiable.
The AR surface — videoOne of five surfaces on a single set of business rules: web, native mobile, this augmented-reality experience, Amazon TV and Alexa, and Apple Watch. Multi-surface case →
BUILD 09 / ARTEFACTKEEP EITHER WAY
Not ready to talk? Take the checklist.
One page, 15 questions about your mobile app you should be able to answer before your next release — when the last submission actually succeeded, what your crash-free session rate is and who reads it, what happens to a queued action on a cold start, which permissions you request without a visible reason, how far behind the framework has fallen, and who holds the signing certificates.
BUILD 10 / NEXTONE STEP
Tell us what the app must do offline, and which stores it must pass.
A free 30-minute working session, not a sales call. We tell you which of your requirements is the binding constraint and whether cross-platform is still the right answer. You keep a one-page read either way: the framework recommendation with its conditions, the store-review risks we would fix first, and the one offline behaviour we would design before anything else.