A regional grocery chain
A Flutter shopping app rebuilt from two diverging native codebases into one, shipping to iOS and Android on the same day.
- Engagement
- 18 weeks
- Year
- 2026
- Disciplines
- App Dev · UI/UX · Backend
- Feature parity gap
- 6 weeks → 0 releases
The problem
The client ran separate Swift and Kotlin apps built by different contractors two years apart. The iOS app was six weeks ahead on features and the Android app had a checkout flow nobody could explain; every product decision had to be specified, built and QA'd twice, and roughly 60% of their mobile budget was going into keeping the two in step rather than into anything a customer would notice.
What we did
- 01
Prove the hard screen first
Before committing to a rewrite we built one screen in Flutter — the basket, which carries live pricing, substitutions and a delivery-slot picker, and was the most animation-heavy thing in either app. Two weeks, both platforms, running on real devices in their office. That screen is what made the decision, rather than a slide comparing frameworks.
- 02
Migrate by flow, not by rewrite
Flutter was embedded into the existing native apps and flows were replaced one at a time behind the same navigation. Browse went first, then basket, then checkout. Every one of those was a normal release with a rollback path — at no point was there a big-bang cutover, and the store listing never changed.
- 03
One design system, drawn once
Flutter renders its own widgets rather than delegating to UIKit and Material, so the design system is a single set of Dart components and the two platforms cannot drift. Their designer stopped maintaining two Figma libraries in the second month.
- 04
60fps as a build gate, not an aspiration
Frame timings from integration tests run on physical devices in CI, and a regression past 16ms on the scroll-heavy screens fails the build. Cross-platform frameworks earn their reputation for jank when nobody measures; this is the check that stops it becoming true.
What changed
Both platforms have shipped from one codebase and one release train since week fourteen. The team that was maintaining two apps now builds features, and the first thing they used the recovered capacity for was the recipe-to-basket flow that had been on the roadmap for a year.
- Codebases maintained
- 21
- Feature parity gap
- 6 weeks0 releases
- Median release cycle
- 5 weeks9 days
- Crash-free sessions
- 98.1%99.7%
- App Store rating
- 3.84.6
Built with
- Flutter
- Dart
- Riverpod
- Firebase
- Node.js
- Fastlane
“They built our worst screen in two weeks and handed us a phone. That was the entire business case — we stopped arguing about frameworks and started arguing about the basket, which is the argument worth having.”