[Flutter - part 2] Understanding Stateful Widget

[Flutter - part 2] Understanding Stateful Widget

Previously in my post about "understanding stateless" widgetđź”— we saw how they are immutable configurations. But, have you ever wondered how your UI gets updated? Well, that's exactly where Stateful Widget comes in.

Stateful Widget

By definition, a stateful widget is one that has a mutable state. It can store data that can change synchronously and throughout its lifetime.

Just like a StatelessWidget creates an Element called Stateless Elementwhich mounts the widget on the element tree, the same way Stateful Widget does something similar, but with an extra step.

Instead of creating a stateless element, the StatefulWidget creates a StatefulElement, which is then mounted on the element tree. After that, this element calls createState() to produce a State object.

LifeCycle of a Stateful Widget

1. createState()

Called when Flutter needs a fresh State object for your widget.
It links your immutable StatefulWidget with its mutable State.

2. initState()

This is the first method that runs inside the State class.
It’s used for initial setup — starting animations, fetching data, initializing controllers, or subscribing to streams.
Always call super.initState() first.

3. didChangeDependencies()

Called after initState() and again whenever any inherited dependencies (like Theme, MediaQuery, or Provider) change.
Perfect for cases where your widget depends on external context.

4. build()

Defines the widget tree that describes your UI.
This method can run multiple times (for example, after calling setState()).

5. setState()

Used to update the UI.
When something changes (like a counter value), you wrap that change inside setState(() { ... }).
This marks the widget as dirty, triggering a rebuild via the framework.

6. didUpdateWidget

Called when the parent rebuilds the widget with new parameters.
You can compare oldWidget and widget to detect changes in configuration.

7. dispose()

The final cleanup step.
Called when the widget is permanently removed from the tree.
Cancel timers, dispose controllers, and clean up streams here.
Always call super.dispose() at the end.

Example

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

As you can see, title and count are used in the widget. Title comes from the widget itself while _counter comes from the state object. The StatefulElement maintains a reference to its State, so both the widget’s data title and its changing state _counter can be accessed and displayed.

When you tap the FloatingActionButton, the _incrementCounter() function runs. Inside it, setState() updates _counter and tells Flutter to rebuild the widget.

When you call setState(), Flutter doesn’t immediately rebuild your widget tree.
Instead, it marks that widget’s element as dirty, meaning this part of the UI is out of sync with its current state and needs to be rebuilt.

Flutter keeps track of all the elements that are marked dirty.
Then, during the next frame, it efficiently rebuilds only those widgets, not your entire app.
After rebuilding, those widgets become clean again until another state change happens.

The updated value is then reflected in the UI.

If MyHomePage is rebuild by its parent, the title changes but the stateful element and stateful object remains the same. The state is preserved.

And that's it for this small article about Understanding Stateful Widget... I hope you liked it!

Coming Next: Part 3 - Inherited Widget used in Flutter.


References

StatefulWidget class - widgets library - Dart API
API docs for the StatefulWidget class from the widgets library, for the Dart programming language.
All About Flutter
Learn Flutter Life Cycle, Learn Flutter App Life Cycle, Flutter initstate, Flutter stateful widget

About Me

I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.

To reach me:

LinkedIn: https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/

X (previously Twitter): _fz3hra

GitHub: https://github.com/fz3hra

Cheers,

Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer