Beginner · Flutter setup and your first app · topic 16 of 50 · 35 min
Stateless and stateful widgets
Explanation
Two kinds of widgets
Some parts of the screen never change by themselves: a logo, a label, a card that just shows the data given to it. These are StatelessWidgets: everything they show comes from their constructor arguments. Other parts change in response to the user or to data: a counter, a checkbox, a form, a loading spinner. These need state: data that can change while the widget is on screen. That is what StatefulWidget is for.
The counter, line by line
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _cups = 0; // the state
void _addCup() {
setState(() { // tell Flutter the state changed
_cups++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Chai counter')),
body: Center(child: Text('Cups sold today: $_cups')),
floatingActionButton: FloatingActionButton(
onPressed: _addCup,
child: const Icon(Icons.add),
),
);
}
}- CounterPage is the widget. It is immutable and only knows how to create its State.
- _CounterPageState holds _cups. It lives as long as this part of the tree stays on screen.
- Tapping the button calls _addCup, which changes _cups inside setState.
- setState marks the widget as needing a rebuild; Flutter calls build again and the new number appears.
The State lifecycle
| Method | When | Use for |
|---|---|---|
| initState | Once, when the State is created | Create controllers, start loading data, subscribe to streams |
| build | Every time the widget needs to draw | Describe the UI from the current state |
| didUpdateWidget | When the parent rebuilds with new arguments | React to changed constructor values |
| dispose | Once, when removed from the tree for good | Dispose controllers, cancel subscriptions and timers |
class _SearchState extends State<SearchPage> {
final _controller = TextEditingController();
Timer? _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(const Duration(seconds: 30), (_) => _refresh());
}
Future<void> _refresh() async {
final results = await fetchResults(_controller.text); // slow call
if (!mounted) return; // the user may have left the screen
setState(() => _results = results);
}
@override
void dispose() {
_timer?.cancel();
_controller.dispose();
super.dispose();
}
// ... build and _results omitted
}mounted is true while the State is in the tree. If the user navigates away during a slow network call, calling setState afterwards would throw "setState() called after dispose()"; the mounted check prevents it.
Which one to choose
- Start with StatelessWidget. Switch to StatefulWidget only when the widget itself must remember changing data.
- Keep state as low in the tree as possible, so only a small part rebuilds.
- When several screens share state, move it into a state management solution (module 7) instead of passing it everywhere.
Common mistakes
- Changing state without setState: the screen does not update.
- Calling setState inside build: an infinite rebuild loop or an error.
- Forgetting dispose for controllers, timers and subscriptions: memory leaks.
- Calling setState after an await without checking mounted.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Stateless and stateful widgets · 1/6
A stateless widget shows only what it is given. A stateful widget remembers data that changes.
Prefer a video by a teacher? Find free videos on “flutter stateless vs stateful widget setState lifecycle initState dispose” on YouTube ↗
In real life: A tea stall's daily counter
A tea stall owner wants a simple app to count cups sold, with plus and minus buttons and a reset at closing time. A StatefulWidget holds the count; each button calls setState. The total sale (count times the price) is computed in build from the state, so it is always correct. The owner can rotate the phone or switch apps without losing the count during the day.
class _StallState extends State<StallPage> {
int _cups = 0;
static const _price = 15;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Cups: $_cups'),
Text('Sales: Rs ${_cups * _price}'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(onPressed: _cups == 0 ? null : () => setState(() => _cups--), icon: const Icon(Icons.remove)),
IconButton(onPressed: () => setState(() => _cups++), icon: const Icon(Icons.add)),
],
),
],
);
}
}Practice (0/3 done)
A tap handler runs _count++ without setState. What happens on screen?
Where should a TextEditingController be disposed?
Build a "water tracker" StatefulWidget: a glass count with + and - buttons (minus disabled at 0), a message that changes at 8 glasses ("Goal reached!"), a reset button, and a Timer started in initState that prints a reminder every 10 seconds and is cancelled in dispose.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Project structure and hot reloadFlutter setup and your first app · same module
- Installing FlutterFlutter setup and your first app · same module
- Interview questions: FlutterFlutter careers and interviews
- What state is, and setStateState management
- Provider and ChangeNotifierState management
- PerformanceAnimations and performance
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does setState do?
Answer: Marks the widget as needing a rebuild after the state changes
Flutter then calls build again.
Which method runs once when a State is created?
Answer: initState
Use it for one-time setup.
Why check mounted after an await?
Answer: The widget may have left the tree, and setState would then throw
Avoids "setState() called after dispose()".
When should you choose a StatelessWidget?
Answer: When everything it shows comes from its constructor arguments
Start stateless by default.
Which object holds a StatefulWidget's State between rebuilds?Related
Is setState a bad practice?Related
Answer: No, it is the right tool for ephemeral state
Use it for local state.
What is a widget?Related
Answer: An immutable description of part of the UI
Flutter rebuilds the screen from widgets.
Which API rebuilds a widget only when one value of the model changes?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Flutter setup and your first app”. The module exam comes next.
- Module 4 exam: Flutter setup and your first app · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, Layouts. 50 topics left in the course before the final exam.