Skip to Content
Flutter🔄 State Management💡 Stateless vs Stateful

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íStatelessWidgetStatefulWidget
StateKhông có
RebuildKhi parent rebuildKhi gọi setState()
PerformanceNhẹ hơnNặng hơn một chút
ComplexityĐơn giảnPhứ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 state

5. 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

  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

📝 Tóm tắt

Widget TypeStateKhi nào dùng
StatelessWidgetKhôngUI tĩnh, display-only
StatefulWidgetUI động, interactions

Lifecycle quan trọng:

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