Blog
Why I Chose Flutter, and What It Cost
One codebase gave me two apps, two stores, two toolchains, and no excuse to skip either platform.
I am an Android developer. That is where my real knowledge sits, and it has for years. I shipped a couple of iOS apps a long time ago, back when the tooling looked nothing like it does now, and nothing since.
So the honest first version of the plan for Memset was an Android app. One developer, evenings and weekends, on the platform I already knew.
Then I chose Flutter, and the plan changed under me without asking.
I still think it was the right call. Two and a half years later there is an iOS app and an Android app running from one codebase, and I would choose the same thing tomorrow. What surprised me was the shape of the cost. It had nothing to do with performance, and nothing to do with the framework fighting me. It was what happens to your own decisions once both platforms are genuinely within reach.

The choice itself was not close
Memset is a review planner. It keeps track of what you are learning, decides when each item should come back, and spreads the daily load so you are not handed forty reviews on a Tuesday evening. Almost all of that is logic: intervals, strategies, categories, streak rules, overdue handling. Very little of it is platform-specific.
For an app shaped like that, a single codebase is close to free money. The scheduler does not care what draws the screen. Neither do the ten languages the app ships in, the Firestore sync, or the local SQLite database holding your review history.
I also had a practical reason. I work on this alone, in the hours after a day job, with a designer joining in May 2025. Writing every screen twice was never going to happen. Flutter supports both mobile platforms from one Dart codebase, and for one person that is a scheduling decision more than a technical preference.
So the choice took about an evening. The consequence took two years to understand.
The ability becomes an obligation
When you build a native Android app, iOS is a separate project. Different language, different framework, different skills, months of work you have not done. Skipping it is easy, and it feels reasonable, because it genuinely is a different job.
Flutter removes that wall. The iOS build is one command away. The screens already exist. The scheduling logic already works. What stands between you and the App Store is a developer account, a signing setup, and a review queue.
At which point “I only ship Android” stops being a technical fact and turns into a choice you have to defend.
Flutter did not give me a second platform. It took away my excuse for not having one.
I felt that every week through 2025. Each week I did not start the iOS release was a week I knew I was leaving users, downloads, and revenue on a table I could already reach. The ability becomes an obligation. The opportunity was real, and being real is exactly what made it a headache. The capability was sitting in my repository and I was not using it.
That pressure appears in no framework comparison I have read. It belongs in one. If you are the sort of developer who cannot leave an obvious opportunity alone, Flutter will hand you a second full product to ship, support, and maintain, and it will feel like your own idea.
Shipping into a store I did not grow up in
So I committed to both, and the iOS half was the part I had to learn from nothing.
The Dart was not the problem. The Dart was the same Dart. What I had to learn was everything wrapped around the binary: App Store Connect, certificates and provisioning profiles, TestFlight, privacy declarations, screenshot sets for every device size Apple asks about, and the App Store Review Guidelines, which are a document you eventually read properly instead of skimming.
Coming from Google Play, none of it was conceptually hard. It was unfamiliar in the specific way that turns a two-hour task into a two-evening task, over and over, for months. My mental model of how an app gets released was Android-shaped, and every Apple step had to be rebuilt from scratch.
The iOS version reached the App Store in March 2026, and the first public release went live that May. Android followed in June. For someone whose core knowledge is Android, that order still looks strange written down.
Two update treadmills, not one
The maintenance surprise arrived later, and it is the cost I would most want another developer to see coming.
A Flutter app does not replace the native platforms. It sits on top of them. You maintain both layers, and they move on schedules that have nothing to do with each other.
The native side brings the Android toolchain, with Gradle and the Android Gradle Plugin on their own upgrade path, plus Xcode versions that arrive on Apple’s calendar rather than yours, plus Google Play’s target API level requirements, which are a deadline whether or not you were planning a release.
The Flutter side brings the SDK, which ships breaking changes on its own cadence, and then roughly forty direct packages sitting on top of it. Some of those move quickly. Riverpod is on version 3. go_router is on its seventeenth major release. Every one of those major version numbers was somebody’s migration afternoon.
Two update treadmills, and neither cares that you were mid-feature. A quiet month for the product can still be a busy month for the build.
This is arithmetic rather than a grievance. One codebase saved me an entire second app. It did not save me from the two operating systems underneath it, and I had not priced that in.
What one codebase actually bought
Set against all of that, the payoff was bigger than I expected, and it kept compounding. Every piece of logic in the app exists once. The spaced repetition scheduling, the strategies deciding how intervals grow, the category balancing that keeps a day’s load survivable, the streak rules, the overdue behaviour. One implementation, one set of bugs, one fix.
Ten languages, translated once, across thirteen App Store locales. A design system built once with my designer and then reused on both stores. Firebase wiring once. When I corrected how an overdue review interacts with a streak, both platforms inherited the fix from the same commit.
That last part is worth sitting with, because it is the real argument for cross-platform work and it is quieter than the marketing version. The value is not that you write the app once. It is that you keep being right on both platforms at the same time, for years, without remembering to.
The edges stay platform-shaped
Some surfaces refuse to be shared, and pretending otherwise wastes weeks. Notification permissions behave differently on each operating system, and so does whatever the device decides to do with a scheduled reminder when it starts protecting its battery. Signing, store metadata, and review are separate processes with separate failure modes. Memset carries platform-permission and device-information packages precisely because some questions can only be answered by the OS you happen to be standing on.
Releases stay doubled as well. One urgent fix means two submissions, two review queues, and two clocks you do not control. One store might clear it in a day while the other takes three, so for a stretch your users are running different versions of the same app and reporting bugs you already fixed. A single codebase does not buy you a single release. It buys you one source of truth and two negotiations.
None of that is a Flutter flaw. It is the honest remainder. The framework shares your code, not the operating systems.
Would I do it again
Yes, without hesitation.
One person building in evenings ended up with a genuinely cross-platform product in ten languages behaving the same way on both stores. Native development would not have produced that, and I am not confident anything else would either.
I would give different advice than the advice I took, though. The question worth asking before you choose Flutter is not whether it can build your app, because it almost certainly can. The question is whether you are ready to run one codebase and two release processes, indefinitely, including the store you have never shipped to and the toolchain you do not enjoy.
If the honest answer is that you only want one store, native is simpler and there is nothing wrong with that. If you want both, Flutter is the cheapest path I know for a single developer, provided you count the second store as part of the price instead of treating it as a bonus.
I counted it late. I would still pay it.
Both versions of Memset come out of that one codebase, and what the codebase does is keep review timing manageable when your week is not. Whichever phone you happen to carry, the app is there, and getting it onto the second one is the part of this story I would not skip again.