
Flutter: Stop the Flickering! (Optimizing Rebuilds)
Why does the image reload when I just updated a counter? Fix flickering by avoiding Futures in build(), using const constructors, and AutomaticKeepAliveClientMixin.

Why does the image reload when I just updated a counter? Fix flickering by avoiding Futures in build(), using const constructors, and AutomaticKeepAliveClientMixin.
Establishing TCP connection is expensive. Reuse it for multiple requests.

Yellow stripes appear when the keyboard pops up? Learn how to handle layout overflows using resizeToAvoidBottomInset, SingleChildScrollView, and tricks for chat apps.

Push works on Android but silent on iOS? Learn to fix APNs certificates, handle background messages, configure Notification Channels, and debug FCM integration errors.

Class is there, but style is missing? Debugging Tailwind CSS like a detective.

It was a simple app fetching a list from an API. But whenever I clicked the 'Like' heart button in the list, the entire screen flashed (Flicker) subtly. Worse, if the list had images, they turned white and reloaded.
"I only toggled a tiny heart icon. Why is the entire Universe (Screen) redrawing?"
This was my mistake of misunderstanding Flutter's Rebuild Mechanism.
FutureBuilderThe #1 most common mistake. Putting an async function directly into the future parameter of FutureBuilder.
class MyPage extends StatefulWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder(
// 😱 getImages() runs EVERY time build() runs!
future: getImages(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(); // Loading Spinner
}
return ListView(...);
},
);
}
}
Think about it.
setState() called.setState means "Redraw the screen (Rebuild)".build() executes again.getImages() is called again.FutureBuilder gets a NEW Future, so it resets state to waiting.Create the Future object ONCE in initState.
class _MyPageState extends State<MyPage> {
// 1. Variable to store Future
late Future<List<Image>> _imagesFuture;
@override
void initState() {
super.initState();
// 2. Call API ONLY ONCE here
_imagesFuture = getImages();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _imagesFuture, // 3. Use stored variable
builder: ...,
);
}
}
Now, even if you call setState 100 times causing 100 build() runs, _imagesFuture remains the same completed object.
FutureBuilder won't go back to loading state. It shows data immediately. Flickering gone.
const ConstructorIf it flickers without data loading involved, it might be unnecessary widget rendering.
The Alpha and Omega of Flutter optimization is const.
// ❌ Bad
Column(
children: [
Text("Static Title"), // Created new every time
MyDynamicWidget(),
],
)
// ✅ Good
Column(
children: [
const Text("Static Title"), // Compile-time constant (Reused)
MyDynamicWidget(),
],
)
Using const is like signing a contract with Flutter Engine: "This widget NEVER changes even if parent rebuilds. Just reuse it."
It drastically reduces the scope of Rebuilds.
When using PageView or TabBarView, switching tabs and coming back often resets scroll position or reloads data.
This is because widgets are Disposed when they leave the screen.
To prevent this, use AutomaticKeepAliveClientMixin.
class MyTab extends StatefulWidget { ... }
// 1. Add Mixin
class _MyTabState extends State<MyTab> with AutomaticKeepAliveClientMixin {
// 2. Return true
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context); // 3. MUST call super.build!
return ListView(...);
}
}
Now this tab stays alive in memory even when hidden. Scroll position preserves perfectly.
If you have a complex drawing or animation inside a list, scrolling might lag.
Wrap that heavy widget in RepaintBoundary.
RepaintBoundary(
child: ComplexGraphWidget(),
)
This tells Flutter: "I will paint myself independently. Don't repaint me just because neighbor widgets changed." It saves GPU cycles.
"Where exactly is the rebuild happening?" Don't guess. See it.
Flutter: Toggle Performance OverlayEven better: "Highlight Repaints" in DevTools. It draws rainbow borders around widgets that are repainting. If it flashes when you do nothing? Optimization Target 100%.
I built a chat room with thousands of messages. Fling scrolling caused stuttering.
IntrinsicHeight for speech bubbles. (This makes layout calculation O(N^2)).ListView.cacheExtent to pre-render items off-screen.const widgets.Result: Solid 60fps. Optimization is mostly about "Stop doing unnecessary math."
Sometimes it's not the widget. It's User.fromJson blocking the UI thread.
If you parse a huge JSON (e.g., 10MB), the animation will freeze.
Use compute to move parsing to a background thread (Isolate).
// UI thread stays free!
final events = await compute(parseEvents, jsonString);
If animation janks, look for heavy computations.
"Flickering" is the biggest factor that makes an app feel "Cheap".
Future in build. Move to initState.const. Turn on 'Prefer const' lint in VS Code.AutomaticKeepAliveClientMixin for Tabs.
CachedNetworkImage for images. (Default Image.network has weak caching).RepaintBoundary"Should I wrap EVERYTHING in RepaintBoundary?" NO. It uses More Memory (VRAM). It creates a separate texture/layer for that widget.
When to use:CustomPaint, Video, Maps.Text or Icon.constI had a Dashboard updating 60 times/sec via Websocket. The UI thread was choking.
Before:return Column(
children: [
HeaderWidget(), // ❌ Re-created 60 times/sec
GraphWidget(data: streamData),
FooterWidget(), // ❌ Re-created 60 times/sec
],
);
After:
return Column(
children: [
const HeaderWidget(), // ✅ Reused (Skipped Rebuild)
GraphWidget(data: streamData),
const FooterWidget(), // ✅ Reused (Skipped Rebuild)
],
);
Build time dropped from 16ms to 2ms.
Flutter Element Tree sees const and says "I know this guy, he hasn't changed," and skips the diffing algorithm entirely.
Using Opacity widget for transparency effects?
It's a performance killer.
It forces a buffer redraw and compositing step.
For animations, FadeTransition is GPU-accelerated and much cheaper.
For static transparency, try Color.withOpacity or Container(color: ...) first.
Use Opacity only as a last resort.