setState explained

setState explained

Every Flutter Engineer has ever used setState to update the UI in his code. But not all of us know what happens behind the scenes when the method is called. In this article, let's outline the steps that follows setState method.

Content:

setState is called

As we talked earlier in the other article, every widget has corresponding element. So does StatefulWidget has StatefulElement. When setState((){}) is called within widget, it makes some debug checks and does the job that is given as input, at the end it calls StatefulElement's markNeedsBuild method:

@protected
void setState(VoidCallback fn) {
    ...
    final Object? result = fn() as dynamic;
    ...
    _element!.markNeedsBuild();
}

StatefulElement's markNeedsBuild

As every element has access to BuildOwner, through StatefulElement's markNeedsBuild, element is firstly checked if it is dirty to check whether setState was called previously. If is called, next line is not reached, if not element marks itself as dirty and asks BuildOwner to add itself to the element dirty list:

abstract class Element {
    bool _dirty = true;
    bool _inDirtyList = false;

    void markNeedsBuild() {
        ...
        if (dirty) {
            return;
        }
        _dirty = true;
        owner!.scheduleBuildFor(this);
    }
}

BuildOwner's scheduleBuildFor

Manager class BuildOwner makes some checks in debug mode, gets BuildScope of the element and calls buildScope's _scheduleBuildFor.

class BuildOwner {
    void scheduleBuildFor(Element element) {
        ...
        final BuildScope buildScope = element.buildScope;
        ...
        buildScope._scheduleBuildFor(element);
        ...
    }
}

BuildScope's _scheduleBuildFor

BuildScope is a class that determines the scope of a BuildOwner.buildScope operation. With the call of _scheduleBuildFor, element is added to scope's _dirtyElements, along side marking the elemet as in dirty list as well.

final class BuildScope {
    final List<Element> _dirtyElements = <Element>[];

    void _scheduleBuildFor(Element element) {
        ...
        if (!element._inDirtyList) {
        	_dirtyElements.add(element);
        	element._inDirtyList = true;	
        }
    }
}

So far we have outlined the steps from setState until the element is added to dirty list. It is time to dive into the process how elements are actually updated at the next frame.