What is the difference between StatelessWidget and StatefulWidget?

High PriorityAsked in ~95% of Flutter interviews

3 min read

Flutter Basics

Default to StatelessWidget — it's a pure function of its constructor params. Promote to StatefulWidget only when something genuinely needs to change within the widget over time.

StatelessWidgetStatefulWidget
Internal state✅ (in a separate State object)
Lifecycle hooksJust build()initState, didChangeDependencies, didUpdateWidget, dispose
Triggers own rebuild❌ Only when parent rebuilds✅ Via setState()
Memory costVery cheapSlightly heavier (extra State object)

Why two classes? Widgets are immutable and recreated on every build. The State is the persistent bit that survives across rebuilds — Flutter keeps it alive in the Element tree.


Code in action

// Stateless — purely a function of constructor args
class Greeting extends StatelessWidget {
  final String name;
  const Greeting({required this.name, super.key});

  @override
  Widget build(BuildContext context) => Text('Hello, $name!');
}

// Stateful — owns mutable state, calls setState to rebuild
class Counter extends StatefulWidget {
  const Counter({super.key});
  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _count = 0;

  void _inc() => setState(() => _count++);

  @override
  Widget build(BuildContext context) => Column(
    children: [
      Text('$_count'),
      ElevatedButton(onPressed: _inc, child: const Text('+')),
    ],
  );
}

When to pick which

You need…Use
Render derived from props/props-onlyStatelessWidget
Track counters, toggles, scroll position, form inputStatefulWidget
Hold an AnimationController, TextEditingController, StreamSubscriptionStatefulWidget (you need dispose)
Wrap and re-style other widgetsStatelessWidget
State lives elsewhere (Provider/Riverpod/BLoC)StatelessWidget + watch the store

Rule: if no field needs to mutate and survive across rebuilds, stay stateless.


Common mistakes to avoid

// ❌ StatefulWidget when nothing actually changes
class Title extends StatefulWidget { ... }
class _TitleState extends State<Title> {
  @override
  Widget build(BuildContext context) => Text(widget.text);   // pure render!
}
// ✅ Make it stateless

// ❌ Putting mutable fields directly on a StatelessWidget
class Counter extends StatelessWidget {
  int count = 0;                       // ❌ widgets are recreated on rebuild → reset every time
}

// ❌ Mutating state without setState — UI won't refresh
void _inc() { _count++; }              // ❌ Flutter doesn't know to rebuild

// ❌ Skipping const constructors on stateless widgets
return Padding(padding: EdgeInsets.all(8), child: ...);     // rebuilt every parent build
// ✅ const Padding(padding: EdgeInsets.all(8), child: ...)

// ❌ Forgetting dispose() on controllers in StatefulWidget
late final _ctrl = TextEditingController();
// dispose() override missing → memory leak

Interview follow-ups

  1. Why are widgets immutable, and what's the role of the State object? Immutable widgets mean rebuilds are cheap and predictable — Flutter recreates the widget tree on every build. The State object lives in the Element tree and survives those rebuilds, which is where any mutable, long-lived data lives.

  2. What's the lifecycle order when a StatefulWidget mounts? createState()initState()didChangeDependencies()build(). On parent rebuild with new config: didUpdateWidget() then build(). On removal: deactivate()dispose().

  3. Why prefer StatelessWidget even when convenient to make everything stateful? Smaller memory footprint, no dispose bookkeeping, const constructors enable canonicalisation, and the code is easier to reason about. Stateful is a tool, not a default.

  4. What's the cost of setState? It marks the State's element as dirty, so Flutter rebuilds that subtree on the next frame. It's cheap if your build() is cheap and the subtree is small — push state down to the smallest widget that needs it.


How helpful was this content?

Please sign in to rate this article.