← Flutter & Dart

Flutter State

What does `setState` do, and what exactly happens when you call it?

Show answer — try answering out loud first

setState tells Flutter that a widget's internal state has changed and that it needs to rebuild. It marks the element as "dirty" and schedules a new call to build on the next frame. You should put the state change inside the setState callback, not outside.

When you change a state variable, Flutter doesn't detect it on its own. setState(() { ... }) does two things:

  1. It runs the code you pass to it (where you modify the state).
  2. It marks the widget as "needs to rebuild" so Flutter calls build again and updates the screen.

It's the most basic way to manage state in Flutter. It works great for a widget's local state (a counter, whether a panel is open). For state shared across many widgets, it falls short, and it's better to use a state management solution.

import 'package:flutter/material.dart';

class Toggle extends StatefulWidget {
  const Toggle({super.key});
  @override
  State<Toggle> createState() => _ToggleState();
}

class _ToggleState extends State<Toggle> {
  bool _isOn = false;

  @override
  Widget build(BuildContext context) {
    return SwitchListTile(
      title: Text(_isOn ? "On" : "Off"),
      value: _isOn,
      onChanged: (newValue) {
        setState(() {
          _isOn = newValue; // the change goes INSIDE setState
        });
      },
    );
  }
}

Two frequent mistakes:

  1. Changing the state outside of setState: _isOn = true; without wrapping it. The value changes but the UI doesn't update.
  2. Calling setState during build or after the widget has been unmounted. Calling setState after an await without checking mounted can throw "setState() called after dispose()". The fix: if (mounted) setState(...).

"setState tells Flutter that the widget's state changed, marks the element as dirty, and schedules a rebuild with build. The state change has to go inside the setState callback, otherwise the UI never finds out. I use it for a widget's local state, like a counter or a switch. For state shared across many widgets I prefer a solution like Provider or Riverpod, because setState only rebuilds that one widget."

Quick challenge

What's the difference between these two lines inside a State?

_count++;
setState(() => _count++);
See answer

_count++; changes the value in memory but does not rebuild the UI: the screen will keep showing the old number until something else triggers a build. setState(() => _count++); changes the value and asks Flutter to rebuild, so the screen updates immediately.