Mobile Apps

Why Your Flutter App Janks on Older Android Phones

The app is silk on your test device and a slideshow on your customer’s three-year-old Android. That gap isn’t Flutter being slow — Flutter renders at 60fps on remarkably weak hardware when the code lets it. Jank on older phones is almost always the app doing avoidable work on the one thread that can’t afford it.

Fast phones forgive wasteful code. Old phones invoice you for every frame — and your users hold the old phones.

Rebuilds you didn’t mean to trigger

The most common cause: giant widgets rebuilt wholesale on every small state change. A setState high in the tree rebuilds every child below it, sixty times a second during animation. On a flagship the waste is invisible; on a budget chip it’s dropped frames. The fixes are structural — push state down so rebuilds touch the smallest possible subtree, split monolithic build methods into focused widgets, and use const constructors everywhere the compiler allows, because const widgets skip rebuilds entirely. Selective listening (select in Provider/Riverpod, granular builders) stops your UI from redrawing because an unrelated field changed.

Lists built the expensive way

A scrolling list built with a plain Column or a ListView without the .builder constructor lays out every item up front — hundreds of widgets for the twelve on screen. Older devices pay that bill in startup stutter and scroll jank. ListView.builder and friends build only what’s visible; fixed itemExtent where item heights are uniform makes scroll math nearly free. This one refactor fixes more budget-phone jank than any other single change.

Images doing GPU work per frame

Full-resolution photos decoded into small thumbnails burn memory and decode time old phones don’t have. Give Flutter the target size with cacheWidth/cacheHeight so decoding happens once at the right resolution. And treat certain “free” effects as luxury items: Opacity wrapping complex subtrees, BackdropFilter blurs, and unRepaintBoundary’d shadows force offscreen render passes that flagship GPUs shrug off and budget GPUs visibly can’t.

Heavy work on the UI thread

JSON parsing a large response, image processing, database queries in a build path — anything over a few milliseconds on the main isolate steals frames. Move real computation to a background isolate with compute(), and keep build methods pure layout.

Profile on the hardware that matters — in profile mode

Two testing mistakes create false confidence: profiling on your own fast phone, and judging performance in debug mode (debug Flutter is dramatically slower by design). Test in profile mode on a genuinely low-end device — the sub-$150 Android your actual users hold. Flutter’s DevTools timeline shows exactly which frames blew the 16ms budget and which widgets were rebuilding when they did; the official performance best practices cover the full toolkit.

Budget-phone performance is a discipline, not a patch — it’s why I keep an aging test device in the drawer for every Flutter build I ship. The app store rating doesn’t come from how the app runs on the developer’s phone; it comes from how it runs on everyone else’s.

keep reading

Related articles

Tell me what you want to build

A website, an app, some automation — or something you’re still figuring out. Tell me the problem and I’ll give you a straight answer on what it takes. No pressure, no sales team.

You’ll deal with me directly — usually a reply within a day.