setState() to Update StatefulWidgets
setState is how you tell Flutter that the state of your widget has changed and the UI needs to rebuild. You call it inside a StatefulWidget's State class, and whatever you put inside the setState block updates the widget. Without setState, changes to your variables won't be reflected on screen.
class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}
class _CounterPageState extends State {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: $count', style: TextStyle(fontSize: 32)),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: Text('Increment'),
),
],
),
),
);
}
}
Try it Yourself ->
Toggle Pattern
Toggling a boolean is one of the most common state patterns. Think show/hide password, light/dark mode switch, or expanding/collapsing an accordion. Just flip the boolean inside setState and the UI updates immediately.
class PasswordField extends StatefulWidget {
@override
_PasswordFieldState createState() => _PasswordFieldState();
}
class _PasswordFieldState extends State {
bool obscure = true;
@override
Widget build(BuildContext context) {
return TextField(
obscureText: obscure,
decoration: InputDecoration(
labelText: 'Password',
suffixIcon: IconButton(
icon: Icon(obscure ? Icons.visibility : Icons.visibility_off),
onPressed: () {
setState(() {
obscure = !obscure;
});
},
),
),
);
}
}
Try it Yourself ->
Form Input Pattern
When handling form inputs, setState keeps the UI in sync with what the user types. You store the input value in state, and update it whenever the onChanged callback fires. This pattern works for text fields, dropdowns, checkboxes, and more.
class NameForm extends StatefulWidget {
@override
_NameFormState createState() => _NameFormState();
}
class _NameFormState extends State {
String name = '';
String greeting = '';
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
decoration: InputDecoration(labelText: 'Enter your name'),
onChanged: (value) {
setState(() {
name = value;
});
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: name.isEmpty ? null : () {
setState(() {
greeting = 'Hello, $name!';
});
},
child: Text('Greet'),
),
SizedBox(height: 16),
Text(greeting, style: TextStyle(fontSize: 20)),
],
),
);
}
}
Try it Yourself ->