Flutter has two main widget types. StatelessWidget never changes after it's built; StatefulWidget can rebuild itself when its data changes.
Stateless
class Greeting extends StatelessWidget {
final String name;
const Greeting(this.name, {super.key});
@override
Widget build(BuildContext context) => Text('Hello, $name');
}
Stateful
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0;
@override
Widget build(BuildContext context) => ElevatedButton(
onPressed: () => setState(() => count++),
child: Text('Count: $count'),
);
}
setState() tells Flutter to rebuild the widget with the new data.
Common mistake: Don't do heavy work inside build() — it can run many times. Keep build() fast and pure.
Summary
Use StatelessWidget for fixed UI and StatefulWidget + setState for UI that changes over time.