StatelessWidget vs StatefulWidget in Flutter: A Complete Guide from Basics to Advanced
Understand the differences between StatelessWidget and StatefulWidget, when to use each one, how Flutter rebuilds widgets, and the best…
StatelessWidget vs StatefulWidget in Flutter: A Complete Guide from Basics to Advanced
Understand the differences between StatelessWidget and StatefulWidget, when to use each one, how Flutter rebuilds widgets, and the best practices for building maintainable Flutter applications.
Introduction
One of the very first questions every Flutter developer encounters is:
Should I use
StatelessWidgetorStatefulWidget?
At first glance, they seem similar because both display UI. However, they serve very different purposes.
Choosing the right widget affects:
- Performance
- Code readability
- Maintainability
- State management
- Application architecture
In this article, we’ll explore both widgets in depth — from the basics to advanced concepts — with practical examples and real-world scenarios.
What is a Widget?
In Flutter, everything is a widget.
Buttons, text, images, layouts, pages, and even the application itself are widgets.
Example:
Text("Hello Flutter")
This Text widget is immutable. If you want to display different text, Flutter creates a new widget configuration rather than modifying the existing one.
There are two primary types of widgets you’ll use:
StatelessWidgetStatefulWidget
Understanding StatelessWidget
A StatelessWidget is a widget whose UI does not change after it is built.
In other words:
- No internal mutable state
- UI depends only on constructor parameters
- The widget rebuilds only when its parent provides new data
Think of it as a printed photograph.
Unless someone replaces the photograph, it always looks the same.
Creating a StatelessWidget
class WelcomeText extends StatelessWidget {
const WelcomeText({super.key});
@override
Widget build(BuildContext context) {
return const Text(
"Welcome to Flutter",
);
}
}
The widget simply describes the UI.
When Should You Use StatelessWidget?
Use a StatelessWidget whenever the UI doesn't need to change because of internal state.
Examples include:
- Text labels
- Icons
- Static images
- Logos
- Dividers
- Simple cards
- Reusable UI components
- Buttons whose behavior is handled externally
Example:
class UserAvatar extends StatelessWidget {
final String imageUrl;
const UserAvatar({
super.key,
required this.imageUrl,
});
@override
Widget build(BuildContext context) {
return CircleAvatar(
backgroundImage: NetworkImage(imageUrl),
);
}
}
The widget displays whatever image is passed to it but doesn’t change the image by itself.
Understanding StatefulWidget
A StatefulWidget is a widget whose UI can change over time.
It stores mutable data inside a separate State object.
Whenever the state changes, Flutter rebuilds the widget.
Think of it as a digital clock.
Every second, the displayed time changes.
Creating a StatefulWidget
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() {
return _CounterPageState();
}
}
class _CounterPageState extends State<CounterPage> {
int counter = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("$counter"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
counter++;
});
},
child: const Icon(Icons.add),
),
);
}
}
Every time setState() is called, Flutter schedules a rebuild for this widget.
How StatefulWidget Works
Unlike a StatelessWidget, a StatefulWidget consists of two separate classes.
StatefulWidget
│
▼
State Object
│
▼
Build Method
The widget itself is immutable.
The mutable data lives inside the State object.
This separation allows Flutter to preserve state across rebuilds.
Comparing StatelessWidget and StatefulWidget
FeatureStatelessWidgetStatefulWidgetMutable state❌ No✅ YesCan update UI internally❌ No✅ YesUses setState()❌ No✅ YesLifecycle methodsLimitedRich lifecycleStores mutable data❌ No✅ YesSuitable forStatic UIDynamic UI
Real-World Examples
Example 1: Company Logo
Image.asset("assets/logo.png")
The logo never changes.
Use:
StatelessWidget
Example 2: Counter
0
↓
1
↓
2
↓
3
The value changes.
Use:
StatefulWidget
Example 3: Login Screen
The screen contains:
- TextFields
- Loading indicator
- Error messages
- Password visibility toggle
These values change.
A login screen is commonly implemented as a StatefulWidget or, in larger applications, a StatelessWidget connected to an external state management solution such as Riverpod, Bloc, or Provider.
What Happens When setState() is Called?
Consider this code:
setState(() {
counter++;
});
Flutter performs these steps:
setState()
↓
Mark widget dirty
↓
Schedule rebuild
↓
Call build()
↓
Compare old and new widget trees
↓
Update only necessary UI
Flutter does not redraw the entire application.
Only the affected portion of the widget tree is rebuilt.
Understanding Rebuilds
Suppose your widget tree looks like this:
Scaffold
│
├── AppBar
├── Counter
├── Button
└── Image
When the counter changes:
Scaffold
│
├── AppBar
├── Counter ← Rebuilt
├── Button
└── Image
The image and app bar can often be reused because they haven’t changed.
Flutter is highly optimized for this process.
StatelessWidget Can Still Rebuild
A common misconception is:
Stateless widgets never rebuild.
This is incorrect.
A StatelessWidget rebuilds whenever:
- Its parent rebuilds.
- Its constructor parameters change.
- An inherited dependency (such as
ThemeorMediaQuery) changes.
Example:
class Greeting extends StatelessWidget {
final String name;
const Greeting({
super.key,
required this.name,
});
@override
Widget build(BuildContext context) {
return Text("Hello $name");
}
}
If the parent passes a different name, Flutter rebuilds the widget.
StatefulWidget Lifecycle
A StatefulWidget has several lifecycle methods.
createState()
↓
initState()
↓
didChangeDependencies()
↓
build()
↓
didUpdateWidget()
↓
deactivate()
↓
dispose()
The most commonly used methods are:
initState()
Called once when the state is created.
Useful for:
- API calls
- Animation controllers
- Stream subscriptions
- Initialising variables
didChangeDependencies()
Called when an inherited dependency changes.
Useful when your widget depends on objects like Theme, MediaQuery, or localization.
build()
Builds the UI.
Should be kept fast and free of heavy business logic.
dispose()
Called when the widget is permanently removed.
Use it to clean up resources.
Example:
@override
void dispose() {
controller.dispose();
super.dispose();
}
Common Stateful Widgets
Examples include:
- Checkbox
- Switch
- Radio button
- Slider
- TextField
- PageView
- TabBar
- Animation widgets
- Video player
- Audio player
These widgets change their appearance or behavior based on state.
Why Keep Widgets Small?
Instead of writing:
class HomePage extends StatefulWidget {
...
}
with hundreds of lines inside one build() method, split the UI into reusable widgets.
Example:
Scaffold(
appBar: const HomeAppBar(),
body: const HomeBody(),
bottomNavigationBar: const HomeNavigationBar(),
)
Benefits include:
- Better readability
- Easier testing
- Improved code reuse
- Reduced rebuild scope
Modern Flutter Approach
Years ago, many Flutter screens were implemented as StatefulWidgets.
Today, it’s common to use:
- Riverpod
- Bloc
- Provider
- Cubit
These libraries manage state outside the widget.
Example with Riverpod:
class HomeScreen extends ConsumerWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final counter = ref.watch(counterProvider);
return Text("$counter");
}
}
Even though the UI updates dynamically, the widget itself remains stateless because the state is managed externally.
This approach improves testability and separates UI from business logic.
Performance Considerations
Many developers assume:
StatelessWidget is always faster.
The reality is more nuanced.
Flutter efficiently rebuilds widgets regardless of whether they are stateless or stateful. The primary performance considerations are:
- Avoid unnecessary rebuilds.
- Keep widgets small.
- Use
constconstructors where possible. - Separate frequently changing UI from static UI.
- Profile performance before optimizing.
Choosing the correct widget type based on your requirements is more important than trying to optimize prematurely.
Common Mistakes
❌ Using StatefulWidget for every screen.
❌ Storing business logic inside build().
❌ Calling setState() for unrelated UI changes.
❌ Forgetting to call dispose() for controllers.
❌ Performing API calls inside build().
❌ Creating large widgets with hundreds of lines of code.
Best Practices
- Prefer
StatelessWidgetwhen the widget has no internal mutable state. - Use
StatefulWidgetonly when local state is genuinely needed. - Keep the
build()method focused on describing the UI. - Dispose of controllers, animations, and streams properly.
- Split large widgets into smaller reusable components.
- Use external state management for complex applications.
- Mark widgets as
constwhenever possible.
When Should You Choose Each?
ScenarioRecommended WidgetStatic textStatelessWidgetCompany logoStatelessWidgetProduct cardStatelessWidgetCounterStatefulWidgetTimerStatefulWidgetForm with validationStatefulWidget (or external state management)Loading indicatorStatefulWidget (or external state management)API-driven screenStatelessWidget + Riverpod/Bloc/ProviderEnterprise applicationMostly StatelessWidgets with external state management
Summary
StatelessWidget and StatefulWidget are the building blocks of every Flutter application.
A StatelessWidget is ideal for UI that depends only on input parameters and doesn't manage its own mutable state. A StatefulWidget is designed for UI that changes over time because of local state.
As Flutter applications grow, you’ll often find that many screens can remain stateless by moving state management into dedicated solutions like Riverpod or Bloc. This keeps widgets focused on presentation while business logic lives elsewhere.
Understanding when and why to use each widget type is a fundamental step toward writing clean, scalable, and maintainable Flutter applications.
메타데이터
- post_id
- bb8e2d4b0402
- slug
- statelesswidget-vs-statefulwidget-in-flutter-a-complete-guide-from-basics-to-advanced-bb8e2d4b0402
- url
- https://medium.com/@nisargratani/statelesswidget-vs-statefulwidget-in-flutter-a-complete-guide-from-basics-to-advanced-bb8e2d4b0402
- canonical_url
- https://medium.com/@nisargratani/statelesswidget-vs-statefulwidget-in-flutter-a-complete-guide-from-basics-to-advanced-bb8e2d4b0402
- author_url
- https://medium.com/@nisargratani
- status
- ok
- fetched_at
- 2026-08-21 22:58:37