What is the relation between stateful and stateless widgets in Flutter

What is the Relation Between Stateful and Stateless Widgets In Flutter?

Stateful Widget is a mutable widget that is the reason it can be drawn multiple times within its lifetime. so in this article, we will go through what is the relation between stateful and stateless widgets in Flutter?

What is the Relation Between Stateful and Stateless Widgets In Flutter?

A Stateless widget will never rebuild by itself but can from external events. A Stateful widget can.

BUT any kind of widget can be repainted at any time.

Stateless only means that all of its properties are immutable and that the only way to change them is to create a new instance of that widget. It doesn’t e.g. lock the widget tree.

Consider an image like the below:

Stateless vs Stateful
Stateless vs Stateful
What is a Stateless Widget?

StatelessWidget — A widget that does not require a mutable state.

  • A stateless widget is a widget that describes part of the user interface by building a constellation of other widgets that describe the user interface more concretely. The building process continues recursively until the description of the user interface is fully concrete.

The stateless widget is useful when the part of the user interface you are describing does not depend on anything other than the configuration information in the object itself and the BuildContext in which the widget is inflated. For compositions that can change dynamically, e.g. due to having an internal clock-driven state, or depending on some system state, consider using StatefulWidget.

class GreenFrog extends StatelessWidget {
  const GreenFrog({ super.key });

  Widget build(BuildContext context) {
    return Container(color: const Color(0xFF2DBD3A));
  • StatefulWidget — A widget that has a mutable state.
    • Stateful widgets are useful when the part of the user interface you are describing can change dynamically.
    • When Flutter builds a StatefulWidget, it creates a State object. This object is where all the mutable state for that widget is held.
  • The concept of state is defined by two things:
    • The data used by the widget might change.
    • The data can’t be read synchronously when the widget is built. All states must be established by the time the build method is called.
  • StatefulWidget lifecycle:
    • createState() — When Flutter is instructed to build a StatefulWidget, it immediately calls createState().
    • Creates the mutable state for this widget at a given location in the tree.
    • Subclasses should override this method to return a newly created instance of their associated State subclass:
        State createState() => _MyState();
    • mounted == true — All widgets have a bool this.mounted property. It turns true when the buildContext is assigned. It is an error to call setState when a widget is unmounted. Whether this State object is currently in a tree.
    • bool get mounted => _element != null;
    • initState() — This is the first method called when the widget is created. initState() is called once and only once. It must call super.initState().
      • Initialize data that relies on the specific BuildContext for the created instance of the widget.
      • Initialize properties that rely on these widgets ‘parent’ in the tree.
      • Subscribe to Streams, ChangeNotifiers, or any other object that could change the data on this widget.
    • @override
      initState() {
        // Add listeners to this class
        cartItemStream.listen((data) {
  • didChangeDependencies() — Called when a dependency of this State object changes.
    • This method is also called immediately after initState(). It is safe to call BuildContext.inheritFromWidgetOfExactType from this method.
    • Subclasses rarely override this method because the framework always calls build after dependency changes. Some subclasses do override this method because they need to do some expensive work (e.g., network fetches) when their dependencies change, and that work would be too expensive to do for every build.
      void didChangeDependencies() { }
  • build() — Describes the part of the user interface represented by the widget.
    • The framework calls this method in a number of different situations:
    • After calling initState().
    • After calling didUpdateWidget.
    • After receiving a call to setState.
    • After a dependency of this state object changes e.g., an InheritedWidget referenced by the previous build changes.
    • After calling deactivate and then reinserting the State object into the tree at another location.
    • The framework replaces the subtree below this widget with the widget returned by this method, either by updating the existing subtree or by removing the subtree and inflating a new subtree, depending on whether the widget returned by this method can update the root of the existing subtree, as determined by calling Widget.canUpdate.
    • Typically implementations return a newly created constellation of widgets that are configured with information from this widget’s constructor, the given BuildContext, and the internal state of this State object.
  • didUpdateWidget() — Called whenever the widget configuration changes.
    • If the parent widget rebuilds and requests that this location in the tree update to display a new widget with the same runtime type and Widget.key, the framework will update the widget property of this State object to refer to the new widget and then call this method with the previous widget as an argument.
    • Override this method to respond when the widget changes (e.g., to start implicit animations).
    • The framework always calls build after calling didUpdateWidget, which means any calls to setState in didUpdateWidget are redundant.
  • @mustCallSuper
    void didUpdateWidget(covariant T oldWidget) { }
    • setState() — Whenever you change the internal state of a State object, make the change in a function that you pass to setState: Calling setState notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a build for this State object.
    • If you just change the state directly without calling setState, the framework might not schedule a build and the user interface for this subtree might not be updated to reflect the new state.
      setState(() { _myState = newValue });
  • deactivate(): Deactivate is called when state is removed from the tree, but it might be reinserted before the current frame change is finished. This method exists basically because State objects can be moved from one point in a tree to another.
    • The framework calls this method whenever it removes this State object from the tree. In some cases, the framework will reinsert the State object into another part of the tree (e.g., if the subtree containing this State object is grafted from one location in the tree to another). If that happens, the framework will ensure that it calls build to give the state object a chance to adapt to its new location in the tree. If the framework does reinsert this subtree, it will do so before the end of the animation frame in which the subtree was removed from the tree. For this reason, State objects can defer releasing most resources until the framework calls their dispose() method.
  • This is rarely used.
    void deactivate() { }
  • dispose() — Called when this object is removed from the tree permanently.
    • The framework calls this method when this State object will never build again. After the framework calls, the State object is considered unmounted and the mounted property is false. It is an error to call setState at this point. This stage of the lifecycle is terminal: there is no way to remount a State object that has been disposed of.
    • Subclasses should override this method to release any resources retained by this object e.g., stop any active animations.
      void dispose() {
        assert(_debugLifecycleState == _StateLifecycle.ready);
        assert(() { _debugLifecycleState = _StateLifecycle.defunct; return true; }());

Consider a below image like a below:

  • Stateless Widget: Stateless Widget state creates ONLY ONCE, then it can update values but not state explicitly. This is clear from their structure as well. That’s why it has only one class which extends with Stateless Widget. So if I say, they can never re-run the build() method again.
  • Stateful Widget: Stateful Widget can update their STATE (locally) & values multiple times upon event triggered. That’s the reason, the implementation is also different. In this, we have 2 classes, one is StatefulWidget & the other is its State implementation handler i.e. State<YourWidget>. So if I say, they can re-run the build() method again & again based on events triggered. The below diagram will help you understand the same in a better way.

    Stateful widget and Stateless widget
    Stateful widget and Stateless widget


Keep Learning !!! Keep Fluttering !!!

Still, need support for Flutter Development? Do let us know your flutter requirement.

In this article, we have been through what is the relation between stateful and stateless widgets in flutter?

FlutterAgency.com is our portal Platform dedicated to Flutter Technology and Flutter Developers. The portal is full of cool resources from Flutter like Flutter Widget GuideFlutter ProjectsCode libs and etc.

Abhishek Dhanani

Written by Abhishek Dhanani

Abhishek Dhanani, a skilled software developer with 3+ years of experience, masters Dart, JavaScript, TypeScript, and frameworks like Flutter and NodeJS. Proficient in MySQL, Firebase, and cloud platforms AWS and GCP, he delivers innovative digital solutions.

Leave a comment

Your email address will not be published. Required fields are marked *

Discuss Your Project

Connect with Flutter Agency's proficient skilled team for your app development projects across different technologies. We'd love to hear from you! Fill out the form below to discuss your project.

Build Your Agile Team

Hire Skilled Developer From Us

"*" indicates required fields

✓ Valid number ✕ Invalid number

ready to get started?

Fill out the form below and we will be in touch soon!

"*" indicates required fields

✓ Valid number ✕ Invalid number