- Published on
Flutter Performance Part 2: Putting Your UI on a Diet
- Authors

- Name
- Phat Tran
In Part 1, we learned how to stop guessing and start profiling. Now that you've run the CPU Profiler, you might have discovered that the Flutter Engine is spending way too much time rebuilding parts of your UI that haven't actually changed.
To achieve a butter-smooth 60 FPS (or 120 FPS on modern screens), your app needs to do its work in less than 16 milliseconds per frame. Every unnecessary widget rebuild eats into that precious time budget.
Let's look at the daily techniques you must adopt to put your UI on a diet.
1. The Magic of const
You've probably seen your IDE complain with blue squiggly lines urging you to add const to your widgets. It’s not just a syntax preference—it is a massive performance boost.
When you declare a widget as const, it acts as a compile-time cache. When the Flutter Engine walks down your Widget Tree during a rebuild, it performs a diffing process. If it encounters a const widget, it instantly knows: "This hasn't changed since compile time. I can skip diffing its entire subtree!"
In a heavy banking app dashboard filled with static icons, labels, and generic card backgrounds, adding const can save the engine thousands of unnecessary operations.
2. Stop Using Helper Methods for UI
This is a controversial topic, but the math doesn't lie. Many developers love to organize their code by splitting large build methods into smaller helper functions.
Why is this bad? When you use a helper method, it shares the exact same BuildContext as its parent. If the parent rebuilds (for example, a user types in a text field at the bottom of the screen), every single helper method is called again, rebuilding the entire screen.
The Solution: Extract them into separate StatelessWidget classes.
// ❌ BAD: Uses the same Context. If the parent rebuilds, this rebuilds too.
Widget _buildHeader(String title) {
return Text(title, style: const TextStyle(fontSize: 24));
}
// ✅ GOOD: A standalone class. Combined with 'const', the Engine can completely skip rendering this if the parent rebuilds!
class HeaderWidget extends StatelessWidget {
final String title;
const HeaderWidget({super.key, required this.title});
Widget build(BuildContext context) {
return Text(title, style: const TextStyle(fontSize: 24));
}
}
3. Isolating Paint with RepaintBoundary
Flutter paints widgets together. If a tiny loading spinner is animating continuously on your screen, Flutter might be forced to repaint the static bank balance and transaction list sitting right next to it, 60 times a second!
You can wrap the animating widget in a RepaintBoundary.
- The Good: It creates a dedicated painting layer for the spinner. The spinner can animate all day without triggering repaints for the rest of the screen.
- The Bad: Don't wrap everything in a
RepaintBoundary. Creating a separate layer requires creating a bitmap cache in RAM. Overusing it will bloat your memory usage and actually degrade performance.
4. Smart State Management (BLoC & Signals)
Rebuilding your UI efficiently often comes down to how you manage state. If you are using BLoC, you shouldn't let every state change rebuild your entire screen. Use buildWhen in your BlocBuilder to ensure the widget only updates when a specific piece of data changes.
// Only rebuild this specific card when the balance changes
BlocBuilder<AccountBloc, AccountState>(
buildWhen: (previous, current) => previous.balance != current.balance,
builder: (context, state) {
return Text('\$${state.balance}');
},
);
Alternatively, if you've adopted modern Signals in Flutter, you get fine-grained reactivity out of the box. Signals automatically track which widgets depend on which values, updating only the specific text nodes that changed, completely bypassing the heavy BuildContext rebuild overhead entirely.
Up Next: Freeing the Main Thread
Now our UI is optimized, but what happens when you need to process a massive API response? In Part 3, we will look at Concurrency, Event Loops, and how to use Isolate.run() to prevent your app from freezing when handling heavy data.