<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/how-to-fix-app-jank-a-practical-guide-to-profiling-flutter-apps-with-devtools-oncch6asp" -->

---
title: How to Fix App Jank: A Practical Guide to Profiling...
description: A practical guide to diagnosing and fixing Flutter app jank using Flutter DevTools. Covers the two root causes of jank (UI thread vs raster thread), how to...
canonical: https://daily.dev/posts/how-to-fix-app-jank-a-practical-guide-to-profiling-flutter-apps-with-devtools-oncch6asp
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: How to Fix App Jank: A Practical Guide to Profiling Flutter Apps with DevTools | daily.dev
og:description: A practical guide to diagnosing and fixing Flutter app jank using Flutter DevTools. Covers the two root causes of jank (UI thread vs raster thread), how to...
og:url: https://daily.dev/posts/how-to-fix-app-jank-a-practical-guide-to-profiling-flutter-apps-with-devtools-oncch6asp
og:image: https://api.daily.dev/og/posts/ONcCh6aSP.png
og:image:alt: How to Fix App Jank: A Practical Guide to Profiling Flutter Apps with DevTools
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Fix App Jank: A Practical Guide to Profiling Flutter Apps with DevTools

**[freeCodeCamp](https://daily.dev/sources/freecodecamp)** · 15 min read · 1 upvotes · 0 comments

## Summary

A practical guide to diagnosing and fixing Flutter app jank using Flutter DevTools. Covers the two root causes of jank (UI thread vs raster thread), how to profile in profile mode, reading flame charts in the Performance view and CPU Profiler, hunting unnecessary widget rebuilds with the Inspector, detecting memory leaks in the Memory view, and fixing the most common jank patterns including synchronous isolate work, FutureBuilder misuse, large lists rendered as Columns, and animated Opacity causing SaveLayer overhead. Includes code examples for before/after fixes and a verification workflow.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.freecodecamp.org/news/how-to-fix-app-jank-profiling-flutter-apps-with-devtools>

## Questions this post answers

### How do I fix jank caused by Opacity animations in Flutter?

Replace Opacity with a changing value with FadeTransition, since Opacity forces Flutter to render its child to an offscreen buffer via a SaveLayer operation on every animation frame, which is one of the most expensive raster thread operations. FadeTransition uses the compositor directly without a SaveLayer buffer. On Impeller, now the default rendering backend on iOS and rolling out on Android, the SaveLayer penalty is reduced compared to the older Skia engine, though preferring FadeTransition still holds.

_Developers chasing raster thread jank in Flutter can find profiling walkthroughs like this on daily.dev._

### Why does my Flutter widget rebuild even though it doesn't depend on the state that changed?

A widget rebuilds unnecessarily when it lives in the same subtree as a widget that calls setState, even without depending on that state itself, because Flutter rebuilds the whole subtree under the State object that changed. The fix is extracting the stateful portion into its own smaller widget so only that widget rebuilds, leaving sibling widgets like an expensive list untouched. Enable 'Track widget build counts' in the DevTools Inspector to confirm which widgets rebuild.

_daily.dev surfaces practical Flutter rebuild-optimization guides for developers untangling widget tree performance._

### Why does passing a large decoded object into Isolate.run in Flutter cause performance problems?

Passing an already-decoded object graph into Isolate.run copies that entire object graph across the isolate boundary, and for massive payloads that copying overhead can rival the cost of the offloaded work itself. The safer pattern is passing the raw JSON string into the isolate and parsing it there with jsonDecode, so only the raw string and the final typed list are copied, avoiding an intermediate object graph crossing.

_Teams offloading heavy parsing in Flutter can track isolate patterns like this via daily.dev._

## Similar posts on daily.dev

- [How to Build Scalable and Performant Flutter Applications: A Handbook for Devs](https://daily.dev/posts/how-to-build-scalable-and-performant-flutter-applications-a-handbook-for-devs-0kfs42gni) · freeCodeCamp · 7 upvotes · 0 comments
- [React Native Gesture Handling at 60fps: Debugging Jank on Low-End Android Devices](https://daily.dev/posts/react-native-gesture-handling-at-60fps-debugging-jank-on-low-end-android-devices-am9akm5a4) · Medium · 0 upvotes · 0 comments
- [Master Flutter Performance: Build Faster, Smoother Apps \(Part 3\)](https://daily.dev/posts/master-flutter-performance-build-faster-smoother-apps-part-3--7ixzsec4g) · Medium · 0 upvotes · 0 comments
- [Three flutter widgets you may have never used before](https://daily.dev/posts/three-flutter-widgets-you-may-have-never-used-before-unn1w2tbo) · Flutter Community · 3 upvotes · 0 comments

---

Tags: [#flutter](https://daily.dev/tags/flutter), [#dart](https://daily.dev/tags/dart)

[View this post on daily.dev](https://daily.dev/posts/how-to-fix-app-jank-a-practical-guide-to-profiling-flutter-apps-with-devtools-oncch6asp)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"How to Fix App Jank: A Practical Guide to Profiling Flutter Apps with DevTools","url":"https://daily.dev/posts/how-to-fix-app-jank-a-practical-guide-to-profiling-flutter-apps-with-devtools-oncch6asp","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/how-to-fix-app-jank-a-practical-guide-to-profiling-flutter-apps-with-devtools-oncch6asp"},"datePublished":"2026-07-08T18:24:23.306Z","dateModified":"2026-09-14T08:18:19.884Z","description":"A practical guide to diagnosing and fixing Flutter app jank using Flutter DevTools. Covers the two root causes of jank (UI thread vs raster thread), how to...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/4dd8d195aaa176938d706b81c29dec8b?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/4dd8d195aaa176938d706b81c29dec8b?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"freeCodeCamp","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"freeCodeCamp","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1628412854/logos/freecodecamp","url":"https://daily.dev/sources/freecodecamp"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/how-to-fix-app-jank-a-practical-guide-to-profiling-flutter-apps-with-devtools-oncch6asp","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"flutter,dart","timeRequired":"PT15M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"freeCodeCamp","item":"https://daily.dev/sources/freecodecamp"},{"@type":"ListItem","position":3,"name":"How to Fix App Jank: A Practical Guide to Profiling Flutter Apps with DevTools"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/how-to-fix-app-jank-a-practical-guide-to-profiling-flutter-apps-with-devtools-oncch6asp#faq","mainEntity":[{"@type":"Question","name":"How do I fix jank caused by Opacity animations in Flutter?","acceptedAnswer":{"@type":"Answer","text":"Replace Opacity with a changing value with FadeTransition, since Opacity forces Flutter to render its child to an offscreen buffer via a SaveLayer operation on every animation frame, which is one of the most expensive raster thread operations. FadeTransition uses the compositor directly without a SaveLayer buffer. On Impeller, now the default rendering backend on iOS and rolling out on Android, the SaveLayer penalty is reduced compared to the older Skia engine, though preferring FadeTransition still holds. Developers chasing raster thread jank in Flutter can find profiling walkthroughs like this on daily.dev."}},{"@type":"Question","name":"Why does my Flutter widget rebuild even though it doesn't depend on the state that changed?","acceptedAnswer":{"@type":"Answer","text":"A widget rebuilds unnecessarily when it lives in the same subtree as a widget that calls setState, even without depending on that state itself, because Flutter rebuilds the whole subtree under the State object that changed. The fix is extracting the stateful portion into its own smaller widget so only that widget rebuilds, leaving sibling widgets like an expensive list untouched. Enable 'Track widget build counts' in the DevTools Inspector to confirm which widgets rebuild. daily.dev surfaces practical Flutter rebuild-optimization guides for developers untangling widget tree performance."}},{"@type":"Question","name":"Why does passing a large decoded object into Isolate.run in Flutter cause performance problems?","acceptedAnswer":{"@type":"Answer","text":"Passing an already-decoded object graph into Isolate.run copies that entire object graph across the isolate boundary, and for massive payloads that copying overhead can rival the cost of the offloaded work itself. The safer pattern is passing the raw JSON string into the isolate and parsing it there with jsonDecode, so only the raw string and the final typed list are copied, avoiding an intermediate object graph crossing. Teams offloading heavy parsing in Flutter can track isolate patterns like this via daily.dev."}}]}
```

