FutureBuilder là một widget trong Flutter tự động xây dựng lại giao diện dựa trên trạng thái hiện tại của AsyncSnapshot nhận được từ một Future được cung cấp. Không giống như gọi setState thủ công sau await, FutureBuilder cung cấp cách tiếp cận khai báo: nó đăng ký Future ngay lần render đầu tiên và gọi hàm builder mỗi khi trạng thái thay đổi — tải, lỗi hoặc dữ liệu sẵn sàng. Theo Flutter API Reference (2026), FutureBuilder đặc biệt hữu ích để tải dữ liệu từ mạng, đọc từ cơ sở dữ liệu và bất kỳ thao tác bất đồng bộ nào mà UI cần hiển thị chỉ báo tải, thông báo lỗi hoặc nội dung sẵn sàng.
Những Điểm Chính
FutureBuilder là một widget Flutter tích hợp từ gói widgets, nhận Future
Không giống như StreamBuilder hoạt động với luồng dữ liệu (Stream), FutureBuilder được thiết kế cho các thao tác bất đồng bộ một lần: yêu cầu HTTP, đọc tệp, truy vấn cơ sở dữ liệu. FutureBuilder tự quản lý việc đăng ký Future: ở lần xây dựng đầu tiên, nó bắt đầu Future và theo dõi quá trình hoàn thành. Khi widget bị hủy, FutureBuilder không hủy Future — đó là trách nhiệm của nhà phát triển.
Theo Flutter Cookbook (2026), FutureBuilder được khuyến nghị cho các trường hợp thao tác bất đồng bộ chạy một lần khi khởi tạo màn hình. Đối với các thao tác lặp lại hoặc luồng dữ liệu, hãy sử dụng StreamBuilder. Cả hai widget đều tuân theo cùng một mẫu UI Phản ứng, nhưng FutureBuilder được tối ưu hóa cho các yêu cầu một lần.
Triển khai nội bộ của FutureBuilder đăng ký Future bằng Future.then và catchError. Khi FutureBuilder bắt đầu, nó đặt connectionState thành ConnectionState.waiting và gọi builder với dữ liệu trống. Khi hoàn thành thành công, connectionState chuyển thành ConnectionState.done với dữ liệu. Khi lỗi, snapshot.error được điền với đối tượng lỗi. Mỗi thay đổi kích hoạt xây dựng lại widget.
AsyncSnapshot là một đối tượng chứa mà FutureBuilder truyền cho hàm builder mỗi khi trạng thái thay đổi. Nó chứa tất cả thông tin về trạng thái hiện tại của thao tác bất đồng bộ: liệu tải có đang tiến hành không, dữ liệu nào đã được nhận hoặc liệu có lỗi xảy ra không. Hiểu AsyncSnapshot là chìa khóa để xây dựng UI đúng cách với FutureBuilder.
| Thuộc tính | Loại | Mô tả |
|---|---|---|
| connectionState | ConnectionState | Trạng thái kết nối hiện tại (none, waiting, active, done) |
| data | T? | Dữ liệu nhận được từ Future (null cho đến khi hoàn thành hoặc khi lỗi) |
| error | Object? | Đối tượng lỗi nếu Future hoàn thành với ngoại lệ |
| hasData | bool | true nếu data không null và connectionState là ConnectionState.done |
| hasError | bool | true nếu Future hoàn thành với lỗi |
Enum ConnectionState định nghĩa giai đoạn của thao tác bất đồng bộ. None — trạng thái ban đầu khi Future chưa được bắt đầu (hiếm khi dùng, thường ở lần xây dựng đầu tiên không có initialData). Waiting — Future đang chạy, dữ liệu chưa được nhận. Active — chỉ được StreamBuilder sử dụng cho luồng có dữ liệu một phần. Done — Future đã hoàn thành, dữ liệu có sẵn qua snapshot.data hoặc lỗi qua snapshot.error.
Xử lý đúng tất cả trạng thái của AsyncSnapshot trong hàm builder là yêu cầu bắt buộc cho code sản xuất. Nếu bạn không xử lý trạng thái waiting, người dùng sẽ thấy màn hình trống trong khi tải. Nếu bạn không xử lý hasError, người dùng sẽ nhận được Ngoại lệ mà không có giải thích. Mẫu khuyến nghị: kiểm tra hasError → kiểm tra hasData → hiển thị tải theo mặc định.
FutureBuilder có thể được sử dụng trong nhiều mẫu tiêu chuẩn, mỗi mẫu giải quyết một tác vụ cụ thể. Hãy xem các kịch bản chính: tải dữ liệu khi khởi tạo, tải với bộ nhớ đệm, yêu cầu song song và xử lý lỗi với thử lại.
Mẫu phổ biến nhất — FutureBuilder trong phương thức build của StatefulWidget hoặc StatelessWidget. Future được truyền từ initState hoặc tạo trực tiếp trong build. Điều quan trọng là không tạo Future trong phương thức build mỗi lần xây dựng lại — điều này sẽ dẫn đến các yêu cầu lặp lại. Sử dụng Future được lưu trữ trong trường State.
Để tránh các yêu cầu lặp lại, FutureBuilder có thể kết hợp với CachedNetworkImage hoặc bộ nhớ đệm cục bộ. Sau lần tải đầu tiên, dữ liệu được lưu trong bộ nhớ hoặc SharedPreferences, và FutureBuilder hiển thị dữ liệu đã lưu trong bộ nhớ đệm ngay lập tức trong khi làm mới từ mạng song song. Điều này cải thiện trải nghiệm người dùng nhờ phản hồi tức thì.
Theo pub.dev (2026), bộ nhớ đệm đặc biệt phù hợp cho hình ảnh và danh sách dữ liệu. FutureBuilder với CachedNetworkImageProvider tự động hiển thị hình ảnh đã lưu trong bộ nhớ đệm, và khi không có — chỉ báo tải theo sau là tệp đã tải xuống.
FutureBuilder và quản lý trạng thái thủ công qua setState là hai cách tiếp cận cho UI bất đồng bộ trong Flutter. Mỗi cách đều có ưu điểm và hạn chế riêng. Sự lựa chọn phụ thuộc vào độ phức tạp của màn hình và số lượng thao tác bất đồng bộ.
FutureBuilder thắng ở sự đơn giản: bạn không cần khai báo trường cho trạng thái tải, dữ liệu và lỗi — mọi thứ được quản lý qua AsyncSnapshot. Nó lý tưởng cho các màn hình đơn giản với một thao tác bất đồng bộ (một yêu cầu HTTP, đọc cơ sở dữ liệu). Tuy nhiên, với 5+ thao tác bất đồng bộ trên một màn hình, FutureBuilder tạo ra sự lồng ghép quá mức — dẫn đến một “Kim tự tháp” các FutureBuilder lồng nhau.
setState với cờ trạng thái thủ công mang lại nhiều kiểm soát và khả năng đọc hơn cho logic phức tạp. Đối với các màn hình có nhiều yêu cầu phụ thuộc (tải người dùng → tải đơn hàng → tải chi tiết đơn hàng), tốt hơn nên sử dụng setState với ChangeNotifier hoặc Bloc. Theo Hướng dẫn Quản lý Trạng thái Flutter (2026), cho các kịch bản phức tạp, Riverpod hoặc Bloc được khuyến nghị thay vì FutureBuilder vì chúng cung cấp sự tách biệt tốt hơn giữa logic và trình bày.
Hãy xem xét một ví dụ thực tế về FutureBuilder để tải danh sách người dùng từ REST API. Code minh họa cách xử lý đúng cả ba trạng thái AsyncSnapshot: tải, lỗi và dữ liệu sẵn sàng.
class UserListPage extends StatefulWidget {
const UserListPage({super.key});
@override
State<UserListPage> createState() => _UserListPageState();
}
class _UserListPageState extends State<UserListPage> {
final Future<List<User>> usersFuture = UserRepository().fetchUsers();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Người dùng')),
body: FutureBuilder<List<User>>(
future: usersFuture,
builder: (context, AsyncSnapshot<List<User>> snapshot) {
if (snapshot.hasError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text('Lỗi: ${snapshot.error}'),
],
),
);
}
if (snapshot.hasData) {
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(backgroundImage: NetworkImage(users[index].avatarUrl)),
title: Text(users[index].name),
subtitle: Text(users[index].email),
);
},
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
Trong ví dụ, FutureBuilder xử lý cả ba trạng thái. Khi lỗi, một biểu tượng với thông báo lỗi được hiển thị. Khi tải thành công — ListView với hình đại diện và tên. Trong khi tải — CircularProgressIndicator. Future được khai báo là trường lớp, ngăn chặn việc gọi lặp lại khi xây dựng lại. Mẫu này bao phủ 90% kịch bản sử dụng FutureBuilder trong ứng dụng di động.
Câu Hỏi Thường Gặp
FutureBuilder gọi builder mỗi khi trạng thái Future thay đổi: lần đầu khi tạo (connectionState: none hoặc waiting), lần thứ hai khi hoàn thành (connectionState: done). Nếu widget cha được xây dựng lại, FutureBuilder cũng được xây dựng lại. Để tránh các lần gọi lặp lại, hãy đảm bảo Future được tạo bên ngoài phương thức build — nếu không mỗi lần gọi build sẽ tạo một Future mới.
Lưu trữ Future trong trường StatefulWidget (trong initState) hoặc sử dụng ghi nhớ. Nếu Future được tạo bên trong phương thức build, mỗi lần gọi build sẽ tạo một Future mới và FutureBuilder sẽ khởi động lại thao tác bất đồng bộ. Đối với StatelessWidget, sử dụng gói cached_future hoặc widget keep-alive để Future chạy một lần bất kể việc xây dựng lại.
FutureBuilder được thiết kế cho các thao tác bất đồng bộ một lần (một yêu cầu HTTP, một lần đọc cơ sở dữ liệu). StreamBuilder hoạt động với luồng dữ liệu có thể phát ra nhiều giá trị theo thời gian (trò chuyện, cập nhật giá, định vị địa lý). StreamBuilder hỗ trợ ConnectionState.active cho dữ liệu một phần, trong khi FutureBuilder chỉ hỗ trợ waiting và done.
Đối với nhiều Future song song, hãy sử dụng Future.wait và truyền kết quả vào một FutureBuilder duy nhất. Future.wait nhận một danh sách Future và trả về Future — khi tất cả Future hoàn thành, builder nhận được một mảng kết quả. Đối với các yêu cầu tuần tự, sử dụng chuỗi Future.then trong một Future hoặc FutureBuilder lồng nhau (kém đọc hơn). Một giải pháp thay thế là gói riverpod với AsyncValue cho nhiều trạng thái bất đồng bộ.
FutureBuilder không tự động hủy Future. Để hủy, hãy sử dụng CancelableOperation từ gói async hoặc cơ chế tùy chỉnh qua cờ cancelled trong State. Đặt cờ trong dispose() và kiểm tra nó sau khi Future hoàn thành trước khi gọi setState. Thay vào đó, hãy sử dụng gói riverpod với AutoDispose, tự động hủy các thao tác bất đồng bộ khi rời khỏi màn hình.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm