← Flutter & Dart

Flutter Fundamentals

What's the difference between a `StatelessWidget` and a `StatefulWidget`, and when should you use each one?

Show answer — try answering out loud first

A StatelessWidget has no internal state that changes: it's drawn once with the information it receives and doesn't redraw itself. A StatefulWidget holds mutable state that can change during the widget's lifetime; when that state changes (with setState), Flutter rebuilds it. Use stateful only when the widget needs to change on its own.

  • StatelessWidget: pure description. Everything it needs arrives through its constructor (its parameters). If that data doesn't change, the widget doesn't change. Examples: an icon, a text label, a button that just displays something.
  • StatefulWidget: has an associated State class where information that can change lives: a counter, whether a switch is on, the text of a field. When you call setState(), Flutter runs build again and updates the UI.

Rule of thumb: always start with StatelessWidget. Only move to StatefulWidget when the widget needs to remember something that changes over time and that it controls itself.

import 'package:flutter/material.dart';

// Stateless: just shows what it receives
class Greeting extends StatelessWidget {
  final String name;
  const Greeting({super.key, required this.name});

  @override
  Widget build(BuildContext context) {
    return Text("Hello, $name");
  }
}

// Stateful: holds a counter that changes
class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

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

  void _increment() {
    setState(() {
      _count++; // change the state and request a rebuild
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text("Count: $_count"),
        ElevatedButton(onPressed: _increment, child: const Text("+1")),
      ],
    );
  }
}

Using StatefulWidget for everything "just in case." A stateful widget is heavier and more prone to bugs (forgetting setState, not cleaning up resources in dispose). If the data only comes from outside and doesn't change inside the widget, a StatelessWidget is simpler and more efficient. Another mistake: modifying a state variable without wrapping it in setState, which means the UI won't update.

"A StatelessWidget has no internal state that changes: it's drawn with what it receives through the constructor and doesn't redraw itself. A StatefulWidget has a State class with mutable data, and when I call setState Flutter rebuilds the widget to reflect the change. My rule is to start with stateless and only use stateful when the widget needs to remember something that changes over time, like a counter or a form."

Quick challenge

If you change a state variable but the UI doesn't update, what's the most likely cause?

See answer

That you modified the variable outside of setState(). Changing _count++ directly updates the value in memory, but it doesn't tell Flutter it needs to rebuild. You have to wrap the change: setState(() { _count++; });. (In a StatelessWidget there's simply no setState because it isn't meant to change on its own.)