From Flutter to Vite + React — Rebuilding MyAllyFile From the Bedrock

Eight sprints, one working PWA, and zero regrets about ripping out the native baggage.

By Mike Whobrey

MyAllyFile has been live as a PWA since last October. I made the call to skip native mobile applications when I got a good look at what a "health and medical" app review and requirements actually entail. That decision was easy. What wasn't easy, and took some time, was the Flutter baggage that PWA was still running on top of. Eight sprints later I've ripped all of it out and rebuilt the whole frontend from scratch as a Vite + React PWA.

What does nuking a working app to the bedrock and rebuilding it look like? Keep reading.

Sprint 1: Ripping out the dead weight

The riskiest decision by far was deciding to take a working Flutter app that was already running fine as a PWA and ripping it out — but the baggage it was hauling around in the android/, ios/, and macos/ folders, Fastlane configs, native biometrics, and in-app purchase logic had to go. First move was deleting all of it, except the web build, which I kept around as a reference while I scaffolded a clean Vite + React + TypeScript app. I closed out this sprint with a route shell wired to Firebase Auth and the local emulators. Not much on the surface, but a solid starting point.

Sprint 2: The one feature that actually matters

Next came arguably the most important feature of MyAllyFile: letting a first responder pull up someone's medical profile without needing an account. I wired up the unauthenticated /p/:id PIN gate and /v/:id public viewer.

Sprint 3: Auth and the dashboard

I migrated email/password and Google sign-in, route guards, and a dashboard shell next — so profile owners actually have somewhere to manage things, which is pretty important as well.

Sprint 4: The profile editor

What good is logging in though if you can't do anything? I rebuilt the profile creation wizard on TanStack Query and Tailwind, including full CRUD for medical info, emergency contacts, the PIN editor, and QR code generation for sharing a profile.

Sprint 5: Porting billing to the new stack

While the core functionality of MyAllyFile is free and always will be free, for users who want added peace of mind or notification channels I still needed to migrate the Stripe workflow. I wired Stripe checkout through Firebase's Firestore extension into the new frontend, so upgrading from free to paid still works exactly like it did before — just running on React now instead of Flutter.

Sprint 6: The public-facing stuff

I finally got around to migrating the public pages — although thinking back, I probably should have started here. Too late now. I wired up the blog list and markdown post pages pulling from Firestore, privacy/terms pages, and the newsletter signup flow.

Sprint 7: Cutting Flutter loose for real

Some local dev testing, smokes, and e2e later, it felt like time to 86 the Flutter deploy pipeline entirely. I updated Firebase Hosting to serve the new Vite dist/ output and added Playwright smoke tests to CI so I would stop forgetting to run them (CRS is a real thing). Production deployment is 100% React, and Flutter is out of its misery.

Sprint 8: Making the PWA actually good

Since I had already put in all this work on the migration, I decided to tackle a few things from my hit list as a "cherry on top" sprint:

  • Offline support — encrypted profile caching via IndexedDB, because a medical emergency doesn't wait around for you to have signal or Wi‑Fi.
  • Push notifications — Firebase Cloud Messaging alerts an owner the moment someone views their emergency profile.
  • Locked it down — App Check on Cloud Functions, CI/CD moved to GCP Workload Identity Federation instead of long-lived service account keys.
  • The theme — full dark/light/system theme support across the app.

Net result

The app is noticeably lighter, more performant, and time-to-production only depends on when I can get a fix deployed.

If you're building in a regulated space and wondering whether the app-store path is worth it — see how MyAllyFile works on the web, or read the origin story.