Bỏ qua để đến nội dung

💡 Stateless vs Stateful

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

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

Tiêu chí StatelessWidget StatefulWidget
State Không 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
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:#2196f3

  • 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 { ... }
  • 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 state

Constructor → build()
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:#f44336

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();
}
}

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;
});
},
);
}
}
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),
),
],
),
);
}
}

  1. Ưu tiên StatelessWidget - dùng StatefulWidget chỉ khi cần
  2. Tách state lên cao - lifting state up
  3. Giữ State nhỏ - chỉ lưu những gì cần thiết
  4. Cleanup trong dispose() - tránh memory leaks

Widget Type State Khi nào dùng
StatelessWidget Không UI tĩnh, display-only
StatefulWidget UI động, interactions

Lifecycle quan trọng:

  • initState() → Khởi tạo
  • build() → Xây dựng UI
  • dispose() → Cleanup