StatelessWidget
Simple Widgets That Never Change Their Mind
Open interactive version (quiz + challenge)Real-world analogy
What is it?
A StatelessWidget is a widget that describes part of the UI based solely on its configuration (constructor parameters) and the BuildContext. It has no mutable state -- once created, it cannot change itself. When the parent provides new data, Flutter creates a new instance and calls build again. StatelessWidgets are the workhorse of Flutter apps: simple, predictable, and efficient. They are easy to test because given the same inputs, they always produce the same output.
Real-world relevance
In team_mvp_kit, many custom widgets are stateless. Display components like UserAvatar, ErrorMessage, LoadingIndicator, and EmptyState are StatelessWidgets because they just render data they receive. Even screens that show dynamic data can be StatelessWidgets when paired with BlocBuilder -- the BLoC owns the state, and the widget just renders whatever state it receives. This separation of concerns is a key benefit of Clean Architecture: widgets stay simple and testable.
Key points
- Defining a StatelessWidget — Extend StatelessWidget and override the build method. The build method returns a widget tree. StatelessWidgets are the simplest and most common widget type in Flutter.
- Accepting Data via Constructor — Pass data into a StatelessWidget through its constructor. All properties should be final because the widget is immutable. The parent widget controls what data flows in.
- The const Constructor Advantage — Marking a widget constructor as const tells Flutter that identical widgets with the same parameters are the same object. Flutter can skip rebuilding const widgets entirely, which is a major performance optimization.
- When to Use StatelessWidget — Use StatelessWidget when the UI depends only on the constructor parameters and the BuildContext. If the widget needs to change over time based on user interaction or data updates, use StatefulWidget instead.
- Composing Larger Widgets — Build complex UIs by composing multiple StatelessWidgets together. Each small widget handles one concern, making code readable, testable, and reusable.
- Using BuildContext — The BuildContext gives you access to the widget's position in the tree. Use it to read theme data, screen size, localization, and to navigate. Never store context in a variable outside of build.
- Callback Parameters — StatelessWidgets can accept function callbacks to communicate with parent widgets. The child widget calls the callback when something happens, and the parent decides what to do.
- StatelessWidget Lifecycle — A StatelessWidget has a simple lifecycle: it is created, build is called, and it displays on screen. When the parent rebuilds with new data, a new instance is created and build runs again. There are no lifecycle hooks like initState.
- Stateless in team_mvp_kit — In Clean Architecture, presentation layer widgets are often stateless because BLoC handles the state. BlocBuilder wraps stateless-like rendering: it rebuilds when BLoC state changes, but the widget itself holds no state.
Code example
import 'package:flutter/material.dart';
class UserProfileCard extends StatelessWidget {
final String name;
final String email;
final String? avatarUrl;
final VoidCallback? onTap;
const UserProfileCard({
super.key,
required this.name,
required this.email,
this.avatarUrl,
this.onTap,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Card(
elevation: 2,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
radius: 28,
backgroundImage: avatarUrl != null
? NetworkImage(avatarUrl!)
: null,
child: avatarUrl == null
? Text(
name[0].toUpperCase(),
style: theme.textTheme.titleLarge,
)
: null,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: theme.textTheme.titleMedium,
),
const SizedBox(height: 4),
Text(
email,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
if (onTap != null)
const Icon(Icons.chevron_right),
],
),
),
),
);
}
}Line-by-line walkthrough
- 1. Import the Flutter material library
- 2. Define UserProfileCard extending StatelessWidget
- 3. Declare final String name -- the user's display name, required
- 4. Declare final String email -- the user's email, required
- 5. Declare final String? avatarUrl -- optional avatar image URL
- 6. Declare final VoidCallback? onTap -- optional tap handler for navigation
- 7. Const constructor with super.key and named parameters
- 8. Override the build method, receiving BuildContext
- 9. Get the current theme for consistent styling
- 10. Return a Card widget with elevation for a shadow effect
- 11. Wrap content in InkWell for a tap ripple effect, only active if onTap is provided
- 12. Set borderRadius on InkWell to match the card's rounded corners
- 13. Add Padding of 16 pixels on all sides
- 14. Use a Row to lay out avatar, text, and chevron horizontally
- 15. Create a CircleAvatar with radius 28 for the profile picture
- 16. If avatarUrl is not null, use NetworkImage to load the avatar
- 17. If avatarUrl is null, show the first letter of the name as fallback
- 18. Add a SizedBox with width 16 for spacing between avatar and text
- 19. Wrap the text Column in Expanded so it takes remaining horizontal space
- 20. Set crossAxisAlignment to start so text aligns left
- 21. Display the name with titleMedium text style
- 22. Add a small SizedBox for vertical spacing between name and email
- 23. Display email with bodyMedium style in a muted color
- 24. Conditionally show a chevron_right icon only if onTap is provided
Spot the bug
class NameTag extends StatelessWidget {
String name;
NameTag({required this.name});
@override
Widget build(BuildContext context) {
name = name.toUpperCase();
return Text(name);
}
}Need a hint?
Show answer
Explain like I'm 5
Fun fact
Hands-on challenge
More resources
- StatelessWidget Class (api.flutter.dev)
- Flutter Layouts (docs.flutter.dev)
- Your First Flutter App (docs.flutter.dev)