How to Stop Excessive Rebuilds in Your Flutter App

Md. Al - AminMd. Al - Amin
4 min read

You press a button.
You change one value.
Suddenly your whole screen rebuilds even widgets that had nothing to do with the change.

You might be thinking:

“But I only called setState() once… what’s the problem?”

Flutter is fast but unnecessary widget rebuilds can ruin performance, cause visual flicker, battery drain, and even crash low-end devices.

In this post, we’re going deep into:

  • What actually causes rebuilds

  • Real-world examples of excessive rebuilds

  • Tools to detect them

  • And how to fix them the right way

If you want your Flutter app to feel smooth, battery-friendly, and production-grade this is for you.

How Flutter Really Rebuilds Widgets

Flutter’s UI is declarative.
That means you don’t manually change UI elements you describe what the UI should look like at any given state.

When state changes, Flutter rebuilds affected widgets.

But here’s the catch:

Flutter rebuilds from the top of the widget tree down unless you break it into smaller, optimized parts.

And sometimes, it rebuilds more than you expected.

Common Rebuild Triggers

Here are the most common ways widgets get rebuilt (sometimes unnecessarily):

  • setState()— Rebuilds the entire widget that called it

  • InheritedWidget (e.g. Theme, MediaQuery) — Rebuilds all widgets using it when it changes

  • Provider, Riverpod, Bloc— Depends on how you consume the state — poor use = unnecessary rebuilds

  • Parent widget rebuilds — All child widgets rebuild unless marked const or separated

Real-World Bug: App Feels Laggy on Every Tap

Let’s say you built a shopping cart screen:

class CartScreen extends StatefulWidget {
  @override
  _CartScreenState createState() => _CartScreenState();
}

class _CartScreenState extends State<CartScreen> {
  int quantity = 1;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Cart'),
        ElevatedButton(
          onPressed: () => setState(() => quantity++),
          child: Text('Add'),
        ),
        ProductImage(), // This shouldn't change
        Text('Quantity: $quantity'),
      ],
    );
  }
}

Problem:

When you press “Add”, setState triggers a rebuild of the entire CartScreen, including ProductImage()which is expensive to rebuild.

Solution:

Extract ProductImage into a StatelessWidget, outside the rebuild scope:

@override
Widget build(BuildContext context) {
  return Column(
    children: [
      Text('Cart'),
      ElevatedButton(
        onPressed: () => setState(() => quantity++),
        child: Text('Add'),
      ),
      const ProductImage(), // const prevents rebuild!
      Text('Quantity: $quantity'),
    ],
  );
}

How to Detect Rebuilds

Flutter gives you a few powerful ways to catch excessive rebuilding:

1. Use debugPrintRebuildDirtyWidgets = true;

Add this in main():

void main() {
  debugPrintRebuildDirtyWidgets = true;
  runApp(MyApp());
}

Now every widget rebuild will be printed in the debug console.

2. Wrap widgets in RepaintBoundary

RepaintBoundary(
  child: SomeExpensiveWidget(),
)

This separates the render pipeline Flutter won’t repaint this widget unless its own state changes.

3. Use Flutter DevTools

Go to Flutter DevTools > Performance > Rebuild Stats
You’ll see exactly which widgets rebuilt and how often.

This is gold when debugging complex UI behavior.

Case Study: Flutter App Lagging on List Scroll

A developer built a chat app. Each message item had:

  • Profile picture

  • Name

  • Last message

  • Timestamp

  • Online status

Whenever any new message arrived all messages were rebuilding. Why?

Problem:

They used a ListView.builder, but placed the entire message list inside a widget that updated when any message changed.

BlocBuilder<ChatCubit, ChatState>(
  builder: (context, state) {
    return ListView.builder(
      itemCount: state.messages.length,
      itemBuilder: (context, index) {
        return ChatTile(message: state.messages[index]);
      },
    );
  },
)

Every time the ChatCubit emitted a new message, the entire ListView rebuilt.

Fix:

Use ListView.separated or optimize rebuilds by checking which items actually changed, or use indexed rebuild logic (like flutter_bloc’s buildWhen).

Best Practices to Prevent Excessive Rebuilds

Do

  • Extract widgets into small, reusable components

  • Use const constructors when possible

  • Use Selector or Consumer wisely in Provider

  • Use buildWhen / select to control rebuild triggers

  • Use RepaintBoundary for images, charts, maps

Don’t

  • Put all UI inside one giant build()

  • Forget to mark stateless widgets as const

  • Wrap entire widget trees in BlocBuilder

  • Allow every state change to rebuild the whole tree

  • Let heavy widgets redraw every frame

Quick Tips

  • Use const liberally especially for static widgets

  • Memorize data or cache widgets if they don’t change often

  • Use ValueNotifier and ValueListenableBuilder for simple UI state (faster than full state management for small tasks)

  • Profile with DevTools every time you add new UI logic

Summary: What You Learned

  • Flutter rebuilds widgets declaratively, but you control the scope

  • Rebuilding too much = lag, Jank, bad UX

  • Use tools like debugPrintRebuildDirtyWidgets, RepaintBoundary, and DevTools

  • Optimize using const, smaller widgets, and smart state consumption

Final Thought

Your Flutter app may “work,” but that doesn’t mean it’s efficient.
Understanding how and when widgets rebuild helps you write smoother, cleaner, production-ready code.

So next time you face a lag or Jank that doesn’t make sense check who’s rebuilding. It might just be that innocent widget you forgot to const.

Related Reads

0
Subscribe to my newsletter

Read articles from Md. Al - Amin directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Md. Al - Amin
Md. Al - Amin

Experienced Android Developer with a demonstrated history of working for the IT industry. Skilled in JAVA, Dart, Flutter, and Teamwork. Strong Application Development professional with a Bachelor's degree focused in Computer Science & Engineering from Daffodil International University-DIU.