StatelessWidget vs StatefulWidget
1. StatelessWidget
Widget không có state, không thay đổi theo thời gian:
class GreetingCard extends StatelessWidget {
final String name;
const GreetingCard({super.key, required this.name});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Text('Hello, $name!'),
),
);
}
}Đặc điểm:
- Chỉ có method
build() - Không thể thay đổi sau khi tạo
- Rebuild khi parent rebuild hoặc props thay đổi
2. StatefulWidget
Widget có state, có thể thay đổi và rebuild:
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: _increment,
child: Text('Increment'),
),
],
);
}
}Đặc điểm:
- Có State object riêng
- State persist qua các lần rebuild
- Gọi
setState()để trigger rebuild
3. So sánh
| Tiêu chí | StatelessWidget | StatefulWidget |
|---|---|---|
| State | Không có | Có |
| Rebuild | Khi parent rebuild | Khi gọi setState() |
| Performance | Nhẹ hơn | Nặng hơn một chút |
| Complexity | Đơn giản | Phức tạp hơn |
Visualizing Rebuilds
StatelessWidget StatefulWidget
constructor constructor
│ │
▼ ▼
build() createState()
│
(Rebuilds only when ▼
parent rebuilds) initState()
│
▼
build() ◀─────────┐
│ │
│ setState()
│ │
▼ │
dispose() ───────┘4. Khi nào dùng gì?
Dùng StatelessWidget khi:
- UI chỉ phụ thuộc vào props được truyền vào
- Không có interaction thay đổi UI
- Display-only components
// Good examples
class UserAvatar extends StatelessWidget { ... }
class ProductCard extends StatelessWidget { ... }
class HeaderText extends StatelessWidget { ... }Dùng StatefulWidget khi:
- UI thay đổi theo user input
- Có animations
- Cần lifecycle methods
- Lưu trữ state local
// Good examples
class SearchBar extends StatefulWidget { ... } // Input changes
class AnimatedLogo extends StatefulWidget { ... } // Animation
class FormPage extends StatefulWidget { ... } // Form state5. Widget Lifecycle
StatelessWidget
Constructor → build()StatefulWidget
Constructor → createState() → initState() → build()
↓
[setState() → build()] (có thể lặp nhiều lần)
↓
dispose()6. Lifecycle Methods
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
@override
void initState() {
super.initState();
// Gọi một lần khi widget được tạo
// Khởi tạo data, subscriptions, controllers
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Gọi khi dependencies thay đổi (InheritedWidget)
}
@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// Gọi khi widget config thay đổi
}
@override
void dispose() {
// Cleanup: cancel subscriptions, dispose controllers
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container();
}
}7. Ví dụ thực tế
Toggle Button
class ToggleButton extends StatefulWidget {
const ToggleButton({super.key});
@override
State<ToggleButton> createState() => _ToggleButtonState();
}
class _ToggleButtonState extends State<ToggleButton> {
bool _isOn = false;
@override
Widget build(BuildContext context) {
return Switch(
value: _isOn,
onChanged: (value) {
setState(() {
_isOn = value;
});
},
);
}
}Expandable Card
class ExpandableCard extends StatefulWidget {
final String title;
final String content;
const ExpandableCard({
super.key,
required this.title,
required this.content,
});
@override
State<ExpandableCard> createState() => _ExpandableCardState();
}
class _ExpandableCardState extends State<ExpandableCard> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
ListTile(
title: Text(widget.title), // Access widget properties via 'widget'
trailing: Icon(_isExpanded ? Icons.expand_less : Icons.expand_more),
onTap: () => setState(() => _isExpanded = !_isExpanded),
),
if (_isExpanded)
Padding(
padding: EdgeInsets.all(16),
child: Text(widget.content),
),
],
),
);
}
}8. Best Practices
- Ưu tiên StatelessWidget - dùng StatefulWidget chỉ khi cần
- Tách state lên cao - lifting state up
- Giữ State nhỏ - chỉ lưu những gì cần thiết
- Cleanup trong dispose() - tránh memory leaks
📝 Tóm tắt
| Widget Type | State | Khi nào dùng |
|---|---|---|
StatelessWidget | Không | UI tĩnh, display-only |
StatefulWidget | Có | UI động, interactions |
Lifecycle quan trọng:
initState()→ Khởi tạobuild()→ Xây dựng UIdispose()→ Cleanup
Last updated on