💡 Stateless vs Stateful
1. StatelessWidget
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “Visualizing Rebuilds”flowchart TD subgraph Stateless["📄 StatelessWidget"] S1["constructor"] --> S2["build()"] S3["❌ Rebuilds only when<br/>parent rebuilds"] end
subgraph Stateful["📊 StatefulWidget"] F1["constructor"] --> F2["createState()"] F2 --> F3["initState()"] F3 --> F4["build()"] F4 --> F5{"setState()"} F5 -->|"Loop"| F4 F4 --> F6["dispose()"] end
style Stateless fill:#e8f5e9,stroke:#4caf50 style Stateful fill:#e3f2fd,stroke:#2196f34. Khi nào dùng gì?
Phần tiêu đề “4. Khi nào dùng gì?”Dùng StatelessWidget khi:
Phần tiêu đề “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 examplesclass UserAvatar extends StatelessWidget { ... }class ProductCard extends StatelessWidget { ... }class HeaderText extends StatelessWidget { ... }Dùng StatefulWidget khi:
Phần tiêu đề “Dùng StatefulWidget khi:”- UI thay đổi theo user input
- Có animations
- Cần lifecycle methods
- Lưu trữ state local
// Good examplesclass SearchBar extends StatefulWidget { ... } // Input changesclass AnimatedLogo extends StatefulWidget { ... } // Animationclass FormPage extends StatefulWidget { ... } // Form state5. Widget Lifecycle
Phần tiêu đề “5. Widget Lifecycle”StatelessWidget
Phần tiêu đề “StatelessWidget”Constructor → build()StatefulWidget
Phần tiêu đề “StatefulWidget”flowchart TD C["Constructor"] --> CS["createState()"] CS --> IS["initState()"] IS --> B["build()"] B --> SS{"setState()"} SS -->|"Loop"| B B --> D["dispose()"]
style C fill:#e3f2fd,stroke:#2196f3 style IS fill:#e8f5e9,stroke:#4caf50 style B fill:#fff3e0,stroke:#ff9800 style D fill:#ffebee,stroke:#f443366. Lifecycle Methods
Phần tiêu đề “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ế
Phần tiêu đề “7. Ví dụ thực tế”Toggle Button
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “📝 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