π Mastering Flutter Performance: The Most Expensive Widgets and How to Optimize Them
FlutterPulseThis article was translated specially for the channel FlutterPulseYou'll find lots of interesting things related to Flutter on this channel. Don't hesitate to subscribe!π
"Flutter gives you power. But with great widgets comes great responsibility."
βββEvery dev who's debugged dropped frames π
"Flutter gives you power. But with great widgets comes great responsibility."
β Every dev who's debugged dropped frames π
As Flutter developers, we love how expressive the UI system is. But that expressiveness can come at a cost β performance. If your app starts to stutter or jank, the culprit is often inefficient or overly expensive widgets.
In this post, we'll break down the most expensive widgets in Flutter, explain why they slow things down, and share real-world tips to optimize them.
β οΈ What Does "Expensive" Mean?
In Flutter, expensive refers to widgets that:
- Trigger frequent rebuilds
- Force complex GPU/CPU operations
- Allocate excessive memory
- Cause jank (missed frames >16ms)
Let's dive into the usual suspects.
1. ListView (Without .builder)
β Bad:
ListView(
children: List.generate(1000, (i) => MyTile(i)),
)
This renders all 1000 items at once β memory overload.
β Good:
ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) => MyTile(index),
)
ListView.builder lazily builds only what's visible, recycling views like RecyclerView.
2. CustomPaint & Canvas Drawing
Manual painting is powerful, but also very GPU-intensive, especially when drawing:
- Gradients
- Bezier paths
- Complex shapes
- Shadows
β οΈ Especially expensive when:
- Used in lists or animations
paint()is triggered every frame
π Tip:
- Use
shouldRepaint => falsewhen possible - Cache
PictureRecorderoutput if static
3. BackdropFilter, ShaderMask, ColorFiltered
These widgets use fragment shaders and create offscreen layers. They're very expensive on:
- Low-end Android devices
- High refresh rate UIs
Example:
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(color: Colors.white.withOpacity(0.1)),
)
π Tip:
- Avoid nesting filters
- Use sparingly
- Cache if possible
4. Text Rendering (with RichText or Overflow)
Text widgets are lightweight until you:
- Use
RichTextheavily - Dynamically measure size (
TextPainter) - Nest inside scrollable views with wrapping
π Tip:
- Prefer
Text()overRichTextwhere possible - Set
maxLinesandoverflowexplicitly
5. Rebuild Storms from setState or AnimatedBuilder
A common mistake: putting a setState() high in the tree, causing entire widget trees to rebuild unnecessarily.
π₯ Problem:
setState(() => count++);
This causes everything under the parent widget to rebuild.
β Fix:
- Split into smaller widgets
- Use
ValueListenableBuilder,Selector, orRepaintBoundary
6. Slivers (Used Improperly)
Slivers are powerful but can become expensive when:
- Over-customized without caching
- Used in nested scroll views
π Tip:
- Avoid deeply nested slivers
- Profile them with DevTools before optimizing blindly
7. Nested Layout Builders / Builders inside Builders
Using:
BuilderinsideListView.builderLayoutBuilderon every tileStreamBuilder+FutureBuilderin lists
This introduces unnecessary rebuilds.
π Tip:
- Memoize logic outside
- Use
constwidgets where possible - Avoid rebuilding UI on every tick
π§ͺ Bonus: Use Flutter DevTools to Find Bottlenecks
Open DevTools > Performance Tab > Record a session and look for:
- Long frame render times (>16ms)
- Deep widget rebuild trees
- Overdraw areas (UI elements layered on top of each other)
Final Thoughts π‘
Flutter performance isn't about avoiding widgets β it's about understanding what's costly, and using them intentionally.
You don't need to fear CustomPaint or BackdropFilter. Just be aware of what happens under the hood and optimize where it matters.
π If this post helped you, consider sharing it with your team.
Want a deep dive into Impeller, shader compilation, or Flutter GPU pipelines? Let me know β I'll write it next!