Automotive / Legacy Migration
Two Legacy Apps, One Modern Flutter Platform: Migrating a National Dealership System
How we merged two 5-year-old native apps into a single Flutter platform for one of Australia's largest dealership software companies — rebuilt from scratch, redesigned, and handed off to their engineering org.
Overview
One of Australia’s largest dealership software companies — their platform powers operations for brands like Ford, Audi, and other tier-one manufacturers — acquired a competitor to expand their dealer network. As part of acquisition due diligence, they audited the acquired codebase.
What they found wasn’t pretty.
Two separate native mobile apps. Java on Android, Objective-C on iOS. Five to six years old, with no meaningful updates in the final years. Internal knowledge about how either app worked had been lost — the original developers were long gone, and documentation was nonexistent. The code was fragmented, outdated, and impossible to maintain, let alone extend.
They needed both apps rebuilt. Fast. And they wanted them unified into a single platform.
That’s where we came in.
The Legacy State
The acquired company operated two distinct mobile applications:
Test Drive App — the operational backbone for dealership test drives. Booking management, draft workflows, test drive execution, GPS tracking of vehicles during drives, and the full administrative layer dealerships need to run test drive operations at scale.
Appraisals App — the tool dealers use to assess trade-in and resale vehicles. Vehicle inspections, damage documentation, valuation workflows, and the operational pipeline for processing appraisals through to resale decisions.
Both apps served the same dealer network but existed as completely separate codebases, separate deployments, separate everything. Dealers had to switch between two apps constantly throughout their day.
The client saw the rebuild as an opportunity — not just to modernize, but to rethink the experience entirely.
What We Built
Two Apps Become One
The headline decision: merge both applications into a single Flutter app.
This wasn’t just “put two apps in a tab bar.” The test drive and appraisal workflows are fundamentally different tools with different information architectures. Cramming them into a shared navigation would make both worse.
Instead, we built a top-level app switcher with a card-flip transition. The entire screen rotates on its vertical axis — like flipping a physical card. One side is Test Drives, the other side is Appraisals. Tap the switcher and the screen spins 180°, revealing the other app on the reverse face. Flip again, and you’re back. It’s a physical, tactile interaction that makes context-switching feel instant and intentional rather than disorienting.
Each side maintains its own navigation stack, its own state, its own workflow logic. But shared concerns — authentication, dealer profile, settings, push notifications — live once at the platform level.
The result feels like two purpose-built apps printed on opposite sides of the same card. Dealers download one app, authenticate once, and flip between their two primary tools in a single gesture.
Full Ground-Up Rebuild in Flutter
We chose Flutter deliberately. The client has a large engineering org that would own this codebase after handoff. Maintaining separate Swift and Kotlin codebases — the modern equivalent of the Obj-C/Java mess we were replacing — would recreate the same maintenance burden within a few years. Flutter gives them one codebase, one deployment pipeline, and one set of skills to hire for.
Every feature was rebuilt from scratch against the new Rails backend the client’s team was building in parallel:
Test Drive Operations
- Booking creation and management with draft workflows
- Real-time GPS tracking during active test drives
- Operational dashboards for dealership staff
- Full booking lifecycle — create, schedule, execute, complete
Vehicle Appraisals
- Complete inspection workflows
- Interactive vehicle damage mapping — a visual car diagram where assessors tap body panels, select damage types, and grade severity. The diagram updates in real-time as damage is documented, giving a visual summary of vehicle condition at a glance
- Valuation pipelines
- Photo capture and documentation
Platform Layer
- Deep linking for notifications and cross-system navigation
- Unified authentication across both app contexts
- Push notification routing to the correct app context
- Offline-capable workflows for dealerships with spotty connectivity
Modern Design, Built Collaboratively
The client treated this rebuild as a brand refresh for the acquired product. We worked closely with their in-house design team — they owned the design system and visual direction, we implemented it in Flutter and pushed back where designs didn’t translate well to mobile patterns.
The original apps looked like they were built in 2019 and never touched again — because they were. The new platform is clean, fast, and visually consistent with the client’s broader product family.
Technical Approach
Two Engineers, Amplified by AI
This was a two-engineer engagement from Variant Systems, both working with Claude Code throughout the build. Claude Code handled the high-volume implementation work — screen builds, API integrations, state management boilerplate — while our engineers owned the architecture, reviewed every line, and made the decisions that AI can’t make well: how to structure the app switcher, how to handle shared vs. isolated state, how to design the damage mapping interaction.
The combination meant we moved at a pace that would normally require a much larger team. Two engineers shipping a complete platform rebuild — two apps’ worth of features, unified, redesigned, and production-ready.
Architecture for Handoff
Every architectural decision was made with handoff in mind. The client’s engineering org would inherit this codebase and maintain it for years. That meant:
- Conventional Flutter patterns — no clever abstractions that require tribal knowledge. Standard state management, standard navigation, standard project structure. A new Flutter developer on their team should be able to onboard quickly.
- Clear separation between app contexts — the test drive and appraisal modules are cleanly isolated. A developer working on appraisals doesn’t need to understand test drive internals, and vice versa.
- Comprehensive documentation — unlike the codebase we were replacing, this one came with architecture docs, module guides, and API integration documentation. We weren’t going to recreate the “lost knowledge” problem we were hired to solve.
Results
The platform has shipped and been handed off to the client’s engineering team. It’s currently rolling out across their dealership network.
- 2 legacy codebases → 1 unified Flutter app — dealers install one app instead of two, with instant context switching between test drives and appraisals
- 4 native codebases eliminated — Java Android + Obj-C iOS for both apps, replaced by a single Dart codebase
- Dramatically faster performance — modern framework, optimized rendering, and a clean architecture replaced years of accumulated technical debt. The difference is immediately noticeable
- Modern UI/UX — complete visual redesign in collaboration with the client’s design team, replacing a 5-year-old interface that hadn’t been touched
- Clean handoff — the client’s engineering org took ownership of a documented, well-structured codebase they can maintain and extend independently
- Two-engineer team — delivered what would traditionally require a team of 6-8 (2 iOS + 2 Android + shared backend integration + QA), powered by Claude Code
How We Work
This engagement is the model for how we approach legacy migration: understand the existing system, make the hard architectural calls early (Flutter over native, unified app over separate apps), execute fast with AI-amplified engineering, and hand off a codebase that’s better than what most teams build for themselves.
The client didn’t need a long-term vendor. They have a strong engineering org. They needed a team that could come in, absorb the complexity of two legacy systems, make opinionated decisions about the rebuild, ship it, and get out of the way. That’s exactly what happened.
Sitting on a legacy codebase that’s holding you back? We rebuild and hand off. Let’s talk. Not sure how bad it is? Start with a free code health check.