Stateless and Stateful Widgets#
[TOC]
Stateless Widget#
Definition: A widget that does not change once it is built.
Use Case: Good for static content that depends only on its configuration (constructor parameters) and the parent widget’s context.
Lifecycle: It is built once and does not rebuild unless the parent rebuilds it.
Examples:
Text,Icon,Container(when used with fixed values).
Code Example:
class MyStatelessWidget extends StatelessWidget {
final String title;
MyStatelessWidget({required this.title});
@override
Widget build(BuildContext context) {
return Text(title);
}
}
Stateful Widget#
Definition: A widget that can change its appearance or behavior in response to events, user input, or data changes.
Use Case: Good for interactive widgets that update dynamically, such as forms, animations, or buttons that change when pressed.
Lifecycle: A
StatefulWidgethas two classes:The widget class (immutable).
The State class (mutable, where the logic and changing data live). Flutter rebuilds the UI when
setState()is called inside theStateclass.
Code Example:
class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int counter = 0;
void _increment() {
setState(() {
counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $counter'),
ElevatedButton(
onPressed: _increment,
child: Text('Increment'),
),
],
);
}
}
Key Difference in One Sentence
A StatelessWidget is immutable (cannot change after it’s built).
A StatefulWidget is mutable (can rebuild itself when its state changes).
Here’s a clear side-by-side comparison table of Stateless vs Stateful Widgets in Flutter that you can use for teaching:
Feature |
StatelessWidget |
StatefulWidget |
|---|---|---|
Mutability |
Immutable – cannot change once built |
Mutable – can change during runtime |
Rebuild Trigger |
Rebuilt only when parent widget rebuilds |
Can rebuild itself using |
Lifecycle |
Has only the |
Has both widget class and |
Data Handling |
Displays data that never changes (static content) |
Manages data that may change over time (dynamic content) |
Use Cases |
Static UI like |
Interactive UI like counters, forms, animations, checkboxes |
Performance |
Lightweight and efficient |
Slightly heavier due to state management |
Examples |
|
Counter app, toggle switches, form inputs |
Summary:
Use StatelessWidget for static UI.
Use StatefulWidget for interactive/dynamic UI.
Why a Stateful Widget needs a State class#
A StatefulWidget is split into two parts:
The widget class
Immutable.
Holds configuration data (like constructor parameters).
Exists only to tell Flutter: “Here is how my UI should be built.”
The
StateclassMutable.
Holds the data that can change over time.
Provides the logic for updating the UI.
Calls
setState()to tell Flutter to rebuild the widget tree.
Why not just put everything in one class?#
Flutter’s UI system is built around immutability:
All widgets in Flutter are immutable (their fields cannot change once created).
When something changes, Flutter doesn’t “mutate” the widget — it rebuilds a new widget tree.
If StatefulWidget itself were mutable, it would break this consistency. By separating configuration (StatefulWidget) from behavior (State), Flutter achieves:
Performance efficiency: only the
Stateobject is preserved during rebuilds, not the whole widget.Clarity: you can see which parts are static (the widget class) and which parts are dynamic (the state).
Reusability: the same widget class can be paired with different states in different contexts.
How it works in practice#
When Flutter rebuilds:
The old
Stateobject is kept.The new
StatefulWidgetobject is created (with new config data if needed).The existing
Stateis reattached to this new widget.
That’s why you must inherit from State — so Flutter knows:
where to store your mutable data, and
how to rebuild your widget when that data changes.
In short:#
A StatefulWidget is immutable by design. Its mutable companion, the State class, exists to hold data and logic that can change. That’s why every StatefulWidget must be paired with a State subclass.