Skip to Content

setState trong Flutter

1. setState là gì?

setState() thông báo cho Flutter rằng state đã thay đổi và cần rebuild widget:

setState(() { _counter++; // Thay đổi state }); // Flutter sẽ gọi lại build()

2. Cách hoạt động

User Action → setState() → Framework schedules rebuild → build() called → UI updated
class Counter extends StatefulWidget { @override State<Counter> createState() => _CounterState(); } class _CounterState extends State<Counter> { int _count = 0; void _increment() { setState(() { _count++; // 1. Thay đổi state }); // 2. Flutter gọi build() lại // 3. UI cập nhật với _count mới } @override Widget build(BuildContext context) { return Text('Count: $_count'); } }

3. Quy tắc sử dụng

✅ Đúng

// State thay đổi TRONG setState setState(() { _count++; }); // Có thể thay đổi trước, setState để trigger rebuild _count++; setState(() {}); // Async work trước, setState sau final data = await fetchData(); setState(() { _items = data; });

❌ Sai

// Không gọi setState trong build() @override Widget build(BuildContext context) { setState(() { ... }); // ❌ Error! return Container(); } // Không gọi setState sau dispose() @override void dispose() { super.dispose(); } void _loadData() async { final data = await api.fetch(); setState(() { ... }); // ❌ Widget đã bị dispose! }

4. Xử lý Async với setState

class DataWidget extends StatefulWidget { @override State<DataWidget> createState() => _DataWidgetState(); } class _DataWidgetState extends State<DataWidget> { List<String> _items = []; bool _isLoading = false; Future<void> _loadData() async { setState(() => _isLoading = true); try { final data = await api.fetchItems(); // Kiểm tra widget còn mounted không if (mounted) { setState(() { _items = data; _isLoading = false; }); } } catch (e) { if (mounted) { setState(() => _isLoading = false); } } } @override Widget build(BuildContext context) { if (_isLoading) return CircularProgressIndicator(); return ListView.builder( itemCount: _items.length, itemBuilder: (context, index) => Text(_items[index]), ); } }

5. Tối ưu setState

Chỉ rebuild những gì cần thiết

// ❌ setState ở widget lớn → rebuild toàn bộ class BigWidget extends StatefulWidget { @override Widget build(BuildContext context) { return Column( children: [ ExpensiveWidget1(), ExpensiveWidget2(), CounterDisplay(_count), // Chỉ cái này cần update ], ); } } // ✅ Tách thành widget con class CounterDisplay extends StatefulWidget { @override Widget build(BuildContext context) { return Text('$_count'); // Chỉ rebuild text } }

Sử dụng const widgets

@override Widget build(BuildContext context) { return Column( children: [ const Header(), // const = không rebuild Text('Count: $_count'), // Chỉ cái này rebuild const Footer(), // const = không rebuild ], ); }

6. Ví dụ thực tế

Form với validation

class LoginForm extends StatefulWidget { @override State<LoginForm> createState() => _LoginFormState(); } class _LoginFormState extends State<LoginForm> { String _email = ''; String _password = ''; String? _errorMessage; bool _isLoading = false; void _submit() async { if (_email.isEmpty || _password.isEmpty) { setState(() => _errorMessage = 'Please fill all fields'); return; } setState(() { _isLoading = true; _errorMessage = null; }); try { await auth.login(_email, _password); } catch (e) { if (mounted) { setState(() { _isLoading = false; _errorMessage = e.toString(); }); } } } @override Widget build(BuildContext context) { return Column( children: [ TextField( onChanged: (v) => _email = v, decoration: InputDecoration(labelText: 'Email'), ), TextField( onChanged: (v) => _password = v, obscureText: true, decoration: InputDecoration(labelText: 'Password'), ), if (_errorMessage != null) Text(_errorMessage!, style: TextStyle(color: Colors.red)), ElevatedButton( onPressed: _isLoading ? null : _submit, child: _isLoading ? CircularProgressIndicator() : Text('Login'), ), ], ); } }

Todo List

class TodoList extends StatefulWidget { @override State<TodoList> createState() => _TodoListState(); } class _TodoListState extends State<TodoList> { final List<Todo> _todos = []; void _addTodo(String title) { setState(() { _todos.add(Todo(title: title)); }); } void _toggleTodo(int index) { setState(() { _todos[index].isCompleted = !_todos[index].isCompleted; }); } void _deleteTodo(int index) { setState(() { _todos.removeAt(index); }); } @override Widget build(BuildContext context) { return ListView.builder( itemCount: _todos.length, itemBuilder: (context, index) { final todo = _todos[index]; return ListTile( leading: Checkbox( value: todo.isCompleted, onChanged: (_) => _toggleTodo(index), ), title: Text(todo.title), trailing: IconButton( icon: Icon(Icons.delete), onPressed: () => _deleteTodo(index), ), ); }, ); } }

7. Khi nào KHÔNG dùng setState

  • State chia sẻ giữa nhiều widgets → Dùng Provider, Riverpod, BLoC
  • State phức tạp → Dùng state management library
  • Global state → Dùng state management library

📝 Tóm tắt

Quy tắcMô tả
Gọi trong State classKhông gọi từ bên ngoài
Không gọi trong build()Gây infinite loop
Kiểm tra mountedTrước khi gọi async setState
Tối thiểu hóa scopeChỉ rebuild những gì cần

Pattern cơ bản:

setState(() { _stateVariable = newValue; });
Last updated on