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

🔄 setState

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

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

// 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;
});
// 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!
}

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

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

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

  • 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

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

Pattern cơ bản:

setState(() {
_stateVariable = newValue;
});