Stateful Widgets
Tapping "Watered" on the fern should tick it off and update the count on screen. That widget must remember something between frames and redraw when it changes. Stateless widgets cannot do that; stateful widgets are built for it.
The rule
Change the value inside
setState, and Flutter runsbuildagain.
A stateful widget is two classes:
class WaterCounter extends StatefulWidget {
const WaterCounter({super.key, required this.plant});
final String plant;
@override
State<WaterCounter> createState() => _WaterCounterState();
}
class _WaterCounterState extends State<WaterCounter> {
int count = 0;
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: () => setState(() => count++),
child: Text('${widget.plant}: watered $count times'),
);
}
}- The widget class is immutable, like any widget. It holds the inputs, here
plant. - The State class is created once by
createStateand lives as long as the widget stays in the tree. The changing value,count, belongs here. - Inside the State,
widget.plantreads the widget's inputs. setStateruns your change and marks the State as needing a rebuild, sobuildruns again with the new value.
Two lifecycle methods matter early: initState runs once when the State is created (set up controllers or timers there), and dispose runs when it is removed (clean them up there).
The shortcut
For every piece of data on a screen, trace it: who changes it, and where does it live?
Who changes it?
The user, by tapping the button. So it is state, not an input from the parent.
The trap is writing count++; on its own. The number really does change, but nothing asks Flutter to rebuild, so the screen keeps showing the old value until something else happens to trigger a build.
Drills
Answer fast — trust the rule.
Where should the changing count be declared?
A handler runs count++; without setState. What does the user see?
Inside _WaterCounterState, how do you read the plant passed to WaterCounter?
Where should you start a timer that must be cancelled later?
Remember
- Change the value inside
setState, and Flutter runsbuildagain. - Trace each value: who changes it, and where it lives.
- Trap: changing a field without
setStateleaves the screen stale.
