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.


How helpful was this content?

Please sign in to rate this article.