Flutter : How to Debug which widgets re-rendered on state change

1,896

Solution 1

In flutter, whenever one widget update ; the whole widget tree repaint. So... no.

But you can also introduce "repaint boundaries" manually by inserting in your tree a RepaintBoundary widget. This explicitly tells flutter to create a new painting layer for it's child (which implies memory cache). So that whenever that child updates, it won't repaint it's parent too.

What you can do is instead debug when a repaintboundary is repainted.

For this you can enable repaint rainbow by :

  • pressing t when using flutter run
  • using vscode Dart Code extension with a ctrl/cmd + shift + p and enable repaint rainbow

Solution 2

One thing to note first about rendering: rebuilding and repainting are not the same. Rebuilding involves layout and painting, whereas repainting doesn't involve relayout.

How to check for rebuild

Add a print statement in any build methods that you are interested in.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('MyApp building');                 <-- this
    return MaterialApp(...

And this:

class MyWidget extends StatelessWidget {
  const MyWidget({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('MyWidget building');  //         <-- here 
    return ProgressBar(...

If you notice that there are unnecessary parts of your widget tree getting rebuilt, then the solution is to extract out the parts of the tree that are changing into their own widgets, preferably const widgets.

How to check for repainting

To see what is getting repainted, you can use the Dart DevTools. While you app is running, click the Repaint Rainbow button.

enter image description here

Alternatively, you can use the following flag in your code:

void main() {
  debugRepaintRainbowEnabled = true;  //   <-- set this flag to true
  runApp(MyApp());
} 

The regions that are being repainted have a rainbow border that changes colors on each repaint. As you can see in the animation below, the entire window is getting repainted every time. (The blue line in the center is part of the widget, not the repaint rainbow.)

enter image description here

If you want to limit what is getting painted, you can add a RepaintBoundary widget to your tree like this:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('MyApp building');
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Container(
            child: RepaintBoundary(  //   <-- repaint boundary
              child: ProgressBar(
                barColor: Colors.blue,
                thumbColor: Colors.red,
                thumbSize: 20.0,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Now when you use DevTools and select the Repaint Rainbow button, you'll see that only the ProgressBar widget is getting repainted.

enter image description here

If the repaint is not expensive, then this is probably not something you need to worry about. At least in most of the documentation that I've seen, people say to minimize rebuilds, but hardly anyone suggests adding repaint boundaries. I'd check the timeline and performance in DartDev tools to see if you need it. Check out the linked video below for details.

See also:

Share:
1,896
Flake
Author by

Flake

Updated on December 05, 2022

Comments

  • Flake
    Flake over 1 year

    I am using Redux with Flutter for state management. Whenever I dispatch an action, I want to know which widgets were re-rendered. Is there any way of doing it?