00 Product Engineering & Mobile
Wings & Prayers
A studio-built game shipped to both the App Store and Google Play from a single Flutter codebase, with real-time Firebase leaderboards, type-safe GoRouter navigation, and predictable Riverpod state. Our own product, used as honest proof of engineering.
01 The position before
Wings & Prayers was operating in a position that didn't reflect the actual work.
Wings & Prayers is our own product, an aviation trivia game with a real-time global leaderboard, eight progression ranks, a thirty-goal logbook, and a finishable scoring system. The engineering brief was the hard part. We wanted one codebase that ships native-quality builds to both the iOS App Store and Google Play, server-validated scores so the leaderboard cannot be faked, navigation that survives deep links and back-stack edge cases without a maze of imperative routing, and game state predictable enough that a streak, three lifelines, milestone bonuses, and daily multipliers all stay consistent across screens and app restarts. And it had to clear two independent app-store review pipelines, each with its own rules, rather than one. This is the work we usually do for clients, run against ourselves so we own the proof rather than describing it.
02 The architectural move
What we built, and why this shape.
We built Wings & Prayers in Flutter so a single typed codebase produces both the iOS and Android apps, with platform feel preserved where it matters and the game logic written exactly once. State is managed with Riverpod: the run, the streak, lifelines, milestone tiers, and the daily anchor live in well-scoped providers, so the UI is a pure function of state and a force-quit mid-run resolves to a defined outcome instead of a corrupted session. Navigation runs on GoRouter, which gives the app a declarative, type-safe route table, real deep links into the leaderboard and career dossier, and a back stack that behaves the same on both platforms. Firebase carries the backend: authentication for the optional sign-in, and Cloud Firestore for the global top-100 leaderboard with server-validated scores and auto-generated callsigns, so the competitive surface is trustworthy rather than client-reported. Then we shipped it the unglamorous way, through both review pipelines, fixing what each store flagged until the same product was live in two places.
04 What changed
The position after.
Wings & Prayers is shipped and live on Google Play, with the iOS build run through App Store review on the same codebase. That is the result that matters here: a real, downloadable product, not a prototype or a slide. The architecture decisions held up under their own success, the leaderboard is server-validated and resistant to spoofed scores, the routing handles deep links and the back stack identically across platforms, and the game state stays consistent through milestones, lifelines, and app restarts. Because it is our own product, we make no claims about download or revenue numbers, that honesty is the point. What it proves is narrower and more useful to a prospective client: we ship cross-platform from one codebase, we wire a real-time backend correctly, and we carry a product through two store reviews to launch. The same discipline we would bring to yours.
06 The full story
Why we shipped our own app
Most studios describe their engineering. We wanted to point at it. Wings & Prayers is a product we designed, built, and shipped ourselves, an aviation trivia game with a spinning category wheel, a bank-it-or-risk-it decision after every answer, five milestone tiers, three lifelines, a thirty-goal logbook, eight pilot ranks, and a global leaderboard. None of that is the interesting part for a prospective client. The interesting part is that it is real, it is on the store, and we own every decision behind it.
Building our own product removes the usual asymmetry of a portfolio. There is no client to thank for the parts that worked and no NDA hiding the parts that were hard. The architecture below is exactly the architecture we would propose for a comparable client build, because we made the same calls when our own name was on the line.
One codebase, two stores: Flutter
The central decision was cross-platform from day one. Wings & Prayers ships to both the iOS App Store and Google Play, and we wanted the game logic, the scoring, the streak system, the milestone math, the lifeline rules, written exactly once. Flutter gives us that: a single typed codebase compiled to native on both platforms, with platform feel preserved where users notice and shared everywhere they do not.
This is not a hedge against learning two native stacks. It is a deliberate trade. When one team is shipping the same product to both stores and the value is in the game system rather than deep per-platform hardware work, a single Flutter codebase is the correct call. We have written up the honest version of that trade-off in our Flutter versus native breakdown, and Wings & Prayers is us taking our own advice.
Predictable game state with Riverpod
A trivia run sounds simple until you write it down. At any moment the app is tracking the active streak, three kinds of lifeline with independent counts, the current milestone tier, a bank that grows as you clear tiers, a daily multiplier that consumes on takeoff, and a strike count that ends the run at three. All of that has to stay consistent across multiple screens, survive backgrounding, and resolve cleanly if the player force-quits mid-flight.
We manage it with Riverpod. Each concern lives in a well-scoped provider, and the UI is rendered as a function of that state rather than mutating widgets directly. The practical payoff is that there is no ambiguous in-between: a run is either in progress with a defined state or it is not. Force-quitting to dodge a third strike does not leave a corrupted session, because the daily anchor is consumed at takeoff and the state machine has no undefined branch to fall into. Predictable state is what lets a game like this be fair.
Type-safe navigation with GoRouter
The app has more navigation than a trivia game first appears to need: the wheel, the run, the milestone screens, the logbook, the career dossier, the leaderboard, the hangar, and the legal and account surfaces. We route all of it with GoRouter, which gives us a declarative route table instead of a sprawl of imperative Navigator calls.
Two things made this worth it. First, deep links: the leaderboard and dossier are real destinations that resolve correctly from outside the app, which matters for sharing and for store-listing links. Second, the back stack behaves identically on iOS and Android, so the same code produces the same navigation contract on both platforms rather than two subtly different behaviours we would have to reconcile by hand. Type-safe routing turns a class of runtime navigation bugs into compile-time certainty.
A leaderboard you can trust: Firebase
A global leaderboard is only worth shipping if scores cannot be faked. We built the competitive surface on Firebase, using authentication for the optional sign-in and Cloud Firestore for the top-100 with auto-generated callsigns. Scores are server-validated rather than taken at the client’s word, so the ranking reflects real runs.
That decision shaped the rest of the app. Because the leaderboard is authoritative, the lifetime score and pilot rank that feed it have to be earned through the same validated path, which is why the daily multiplier, the milestone bonuses, and the strike ceiling all live in logic we control rather than client-side conveniences a determined player could spoof. Firebase also keeps the operational surface small: managed auth and a managed datastore mean we spend our attention on the game, not on running infrastructure.
Shipping through two review pipelines
The last mile of a cross-platform product is the part the architecture diagrams leave out: you do not ship once, you ship twice, into two independent review processes with two different rule sets. Getting the same product live on Google Play and through App Store review meant satisfying both, account deletion flows, privacy disclosures, store metadata, and the platform-specific expectations each reviewer holds.
We treat that as engineering, not paperwork. The account-deletion path, the privacy and terms surfaces, and the offline-first behaviour were all built into the product because the stores require them and because they are the right thing to ship anyway. The result is a single Flutter codebase that cleared both gates and is live as a real, downloadable app.
What this proves, and what it does not
We will not invent numbers for our own game. There are no download counts, revenue figures, or user testimonials here, because honesty is a positioning asset and fabricated metrics would corrode the one thing this case study exists to demonstrate.
What it does prove is concrete and exactly the thing a six-figure client is buying: we ship native-quality apps to both stores from one codebase, we wire a real-time backend so the trustworthy parts are actually trustworthy, we route complex navigation with compile-time safety, and we hold game state predictable enough to be fair. Wings & Prayers is the proof we control, built to the standard we sell. If you want that discipline pointed at your product, that is the conversation to have, on mobile app development and the software engineering behind it.
· In their words
"We build our own products on the exact stack and standard we sell. Wings & Prayers exists so we can point at shipped engineering instead of describing it, one Flutter codebase, two app stores, a server-validated leaderboard, and state we trust."