Stateless Widgets
Stateless Widgets
Stateless Widget គឺជា widget ដែលមិនផ្លាស់ប្តូរ state។ ពួកវា immutable និងមាន performance ល្អ។ Build method ត្រូវបានហៅតែម្តងប៉ុណ្ណោះ លុះត្រាតែ parent widget rebuild។
📚 Stateless Widget Theory
Widget Lifecycle (Stateless):
Stateless Widget Lifecycle (Simple!)
1️⃣ Constructor called
↓
2️⃣ build() called
↓
3️⃣ Widget displayed
↓
4️⃣ Parent rebuilds → build() called again
↓
5️⃣ Widget removed from tree (disposed)
Note: No setState(), no state changes!
Only rebuilds when parent changes.
Characteristics:
Feature | Description |
---|---|
Immutability | Properties marked as final , cannot change |
Performance | ⚡ Fast - no state management overhead |
Rebuild Trigger | Only when parent widget rebuilds |
Use Cases | Static UI, labels, icons, layouts |
Memory | Lower memory footprint |
🎯 ពេលណាប្រើ Stateless?
- 📄 Static Content: Text, Images, Icons that don't change
- 🎨 Layout Widgets: Container, Row, Column, Padding
- 🔢 Display Data: Show information from parent (no interaction)
- ⚡ Performance Critical: Lists with thousands of items
- 📦 Reusable Components: Buttons, cards, badges
🎯 Golden Rule: Use Stateless whenever possible! Only use Stateful when you truly need to manage changing state internally.
📝 Stateless Widget Structure
Basic Template:
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('Hello'),
);
}
}
With Constructor Parameters (Recommended):
class GreetingCard extends StatelessWidget {
// All properties must be final (immutable)
final String name;
final String message;
final Color? backgroundColor;
// Constructor with named parameters
const GreetingCard({
Key? key,
required this.name,
required this.message,
this.backgroundColor,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: backgroundColor ?? Colors.blue,
padding: EdgeInsets.all(16),
child: Column(
children: [
Text(
'Hello, ' + name + '!',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(message),
],
),
);
}
}
// Usage
GreetingCard(
name: 'សុខា',
message: 'សូមស្វាគមន៍មកកាន់ Flutter!',
backgroundColor: Colors.green,
)
Why use 'const' constructor?
// Without const - Creates new instance each rebuild
MyWidget(child: Text('Hello'))
// With const - Reuses same instance (better performance!)
const MyWidget(child: const Text('Hello'))
// Flutter can optimize:
// - No unnecessary rebuilds
// - Lower memory usage
// - Faster widget comparison
✅ Real-World Examples
1. Profile Card (Complex Layout)
class ProfileCard extends StatelessWidget {
final String name;
final String role;
final String imageUrl;
const ProfileCard({
Key? key,
required this.name,
required this.role,
required this.imageUrl,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(imageUrl),
),
SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
role,
style: TextStyle(color: Colors.grey),
),
],
),
],
),
),
);
}
}
2. Custom Button Component
class CustomButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
final Color color;
final IconData? icon;
const CustomButton({
Key? key,
required this.text,
required this.onPressed,
this.color = Colors.blue,
this.icon,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: color,
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null) ...[
Icon(icon),
SizedBox(width: 8),
],
Text(text, style: TextStyle(fontSize: 16)),
],
),
);
}
}
// Usage
CustomButton(
text: 'Submit',
icon: Icons.send,
color: Colors.green,
onPressed: () {
print('Button clicked!');
},
)
3. Product Card for E-commerce
class ProductCard extends StatelessWidget {
final String name;
final double price;
final String imageUrl;
final double rating;
final VoidCallback onTap;
const ProductCard({
Key? key,
required this.name,
required this.price,
required this.imageUrl,
required this.rating,
required this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Card(
elevation: 4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image
AspectRatio(
aspectRatio: 16 / 9,
child: Image.network(
imageUrl,
fit: BoxFit.cover,
),
),
Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Name
Text(
name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 8),
// Price & Rating
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'\$' + price.toStringAsFixed(2),
style: TextStyle(
fontSize: 18,
color: Colors.green,
fontWeight: FontWeight.bold,
),
),
Row(
children: [
Icon(Icons.star, color: Colors.amber, size: 16),
SizedBox(width: 4),
Text(rating.toString()),
],
),
],
),
],
),
),
],
),
),
);
}
}
4. Info Badge
class InfoBadge extends StatelessWidget {
final String label;
final IconData icon;
final Color color;
const InfoBadge({
Key? key,
required this.label,
required this.icon,
this.color = Colors.blue,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: color),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 16, color: color),
SizedBox(width: 6),
Text(
label,
style: TextStyle(
color: color,
fontWeight: FontWeight.w600,
),
),
],
),
);
}
}
// Usage
InfoBadge(
label: 'Premium',
icon: Icons.star,
color: Colors.amber,
)
🎨 Composition Pattern
Build complex UIs by combining simple Stateless Widgets:
class UserProfile extends StatelessWidget {
final String name;
final String email;
final String bio;
final String avatarUrl;
const UserProfile({
Key? key,
required this.name,
required this.email,
required this.bio,
required this.avatarUrl,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
_buildAvatar(),
SizedBox(height: 16),
_buildName(),
SizedBox(height: 8),
_buildEmail(),
SizedBox(height: 16),
_buildBio(),
],
);
}
Widget _buildAvatar() {
return CircleAvatar(
radius: 50,
backgroundImage: NetworkImage(avatarUrl),
);
}
Widget _buildName() {
return Text(
name,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
);
}
Widget _buildEmail() {
return Text(
email,
style: TextStyle(color: Colors.grey),
);
}
Widget _buildBio() {
return Text(
bio,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14),
);
}
}
💡 Best Practices:
- ✅ Mark all properties as
final
- ✅ Use
const
constructor when possible - ✅ Keep widgets small and focused (single responsibility)
- ✅ Extract helper methods for complex UI parts
- ✅ Use
required
for mandatory parameters - ✅ Provide default values for optional parameters
- ✅ Add
Key? key
parameter for widget identification
⚠️ Common Mistakes:
- ❌ Forgetting to mark properties as
final
- ❌ Using Stateful Widget when Stateless would work
- ❌ Not using
const
(missing performance gains) - ❌ Creating too large/complex widgets (hard to maintain)
- ❌ Trying to modify properties after creation (compile error!)
💡 ជំនួយ: ប្រើ Stateless Widget នៅពេលដែលអាច។ វាមាន performance ល្អជាង Stateful Widget។ Properties ត្រូវតែ final។ Use const constructor for optimization។