Kivy has a reputation on this site, and it isn't a good one. Toy framework. Fine for a prototype. Nothing you'd ship. I shipped a Kivy app to Google Play in August 2026, and I want to give an honest account of what that stack costs and what it buys — not to argue it's better than Flutter, because for most teams it isn't.
The constraint that led here
I don't own a PC. The entire app was written on a phone.
That single fact eliminates almost every mobile stack in existence. Android Studio doesn't run on Android. Gradle from a touchscreen is not a workflow. Flutter needs a desktop toolchain, and so does React Native. What does run on a phone is a full Python 3.13 interpreter with a real editor. You can't compile an APK there — that part happens on a CI runner, where python-for-android does the packaging. The phone writes code, GitHub builds it. Once you accept that split, Kivy is what's left.
So this isn't "I evaluated the options and chose Kivy." It's "Python was the only thing that ran, and Kivy is what Python has." Worth stating plainly, because it changes how you should read everything below.
What works better than the reputation suggests
Kivy renders identically everywhere. This turned out to be the single most valuable property of the framework. Because Kivy draws its own widgets on an OpenGL surface instead of delegating to platform ones, what appears in the local interpreter is pixel-for-pixel what appears in the packaged build. No layout surprises after a twenty-minute CI run. When your feedback loop is that long, deterministic rendering stops being a nice-to-have.
The iteration loop is genuinely fast. Edit, run, look. No sync step, no emulator boot. For UI work this beats waiting on a Gradle build, and I say that having done both.
KivyMD gets you to acceptable. KivyMD 1.2.0 gives you Material-flavoured components — cards, dialogs, bottom sheets, a navigation drawer — that look deliberate rather than improvised. Not indistinguishable from native. But past the threshold where users read the app as unfinished.
It scales further than people assume. The codebase reached 64+ files and roughly 26,600 lines. Nothing about Kivy pushed back at that size.
What hurts
APK size. Around 53 MB per architecture build, because you're shipping a Python interpreter and the standard library along with your code. Flutter lands lower. If install size is a conversion metric for you, this is disqualifying on its own.
Performance on weak hardware. Kivy's ceiling is below native. Screens that feel smooth on a mid-range phone can stutter noticeably on an older one, and heavy work on the main thread shows up immediately as dropped frames. You budget for this from the start or you retrofit threading later, painfully.
It never feels native. Custom-drawn widgets mean your scroll physics, your text selection, your back-gesture behaviour are approximations of the platform's, not the platform's. Users don't articulate this. They just feel that something is slightly off.
The p4a limits you design around
python-for-android is where the sharp edges live, and they're architectural, not cosmetic. Three that cost me real time:
-
SQLite ships without FTS5. No
MATCH, nobm25(). Full-text search has to be rebuilt onLIKEwith manual document fragmentation. Discover this after you've designed your search layer and you're rewriting it. -
requestsmust be imported inside methods, not at module scope, or the app can fail to start on some devices. An ugly rule that you simply follow. -
Filesystem access goes through
getExternalFilesDir. Writing to/storage/emulated/0/directly is blocked by Scoped Storage. Every path in the project has to respect that.
None of these are unsolvable. All of them are cheaper to know before you write code than after.
Two Pythons in one project
The local interpreter runs 3.13. The packaged build runs 3.11 through p4a. Same source, two runtimes, and the differences surface in irritating places — mostly standard library behaviour that shifted between versions.
The practical rule is to write for the older one and treat the newer as a superset you don't touch. Newer syntax and newer stdlib arrivals are off-limits regardless of what works locally.
So when is this the right call?
For most teams: Flutter. Larger ecosystem, native-feeling UI, smaller binaries, hiring is easier, and the tooling doesn't fight you. That's not a close contest and I'm not going to pretend otherwise.
Kivy earns its place in a narrow band:
- You already write Python and the app is a front-end over Python logic you'd otherwise reimplement.
- You need one codebase running on desktop and mobile without a second UI layer.
- Your constraints rule out a desktop toolchain entirely.
Outside that band, the cost is real and the payoff isn't. Inside it, Kivy is not a compromise — it's the only stack that clears the bar.
The thing worth correcting isn't the ranking. It's the assumption that Kivy can't finish. It can — mine is live on Google Play as Molfar System, targeting API 36 with a minimum of API 24. "Unfashionable" and "unusable" turn out to be different claims.
Top comments (0)