setState() báo cho framework biết state trong State vừa đổi: nó chạy callback đồng bộ, rồi đánh dấu element là dirty (markNeedsBuild).
Flutter gọi lại build() ở frame kế tiếp chứ không rebuild ngay, và nhiều lần setState trong cùng frame chỉ gây một lần build.
Future<void> _load() async {
final data = await api.fetch(); // việc async làm BÊN NGOÀI setState
if (!mounted) return; // widget có thể đã bị dispose
setState(() => _items = data); // callback chỉ gán state, đồng bộ
}Các quy tắc hay bị hỏi:
- Callback không được là async (Flutter báo lỗi) — chạy việc async trước rồi mới setState.
- Gọi setState sau dispose() sẽ ném lỗi → kiểm tra mounted.
- Không gọi setState bên trong build().
Chi phí rebuild tỉ lệ với kích thước subtree: setState ở widget gốc của màn hình sẽ build lại cả màn. Với thứ đổi liên tục (animation, stream), dùng AnimatedBuilder, ValueListenableBuilder, StreamBuilder để khoanh vùng rebuild nhỏ nhất.
Lưu ý: setState chỉ hợp cho state cục bộ của một widget. State cần chia sẻ giữa nhiều widget hoặc nhiều màn nên chuyển lên Provider/Riverpod/BLoC.