FutureBuilder — nó là gì, làm việc với Future trong Flutter

Tác giả: IT Sectr Đã đăng: 2026-07-02 Thời gian đọc: 8 phút

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 — widget Flutter để xây dựng UI dựa trên trạng thái Future qua AsyncSnapshot (none, waiting, active, done)
  • AsyncSnapshot — một đối tượng chứa trạng thái hiện tại của thao tác bất đồng bộ: connectionState, data và error
  • builder — hàm callback được gọi mỗi khi trạng thái Future thay đổi để xây dựng lại UI
  • Xử lý lỗi — AsyncSnapshot.hasError cho phép hiển thị giao diện dự phòng khi thao tác bất đồng bộ thất bại
  • ConnectionState — enum với bốn giá trị: none (không có thao tác), waiting (chờ), active (luồng), done (hoàn thành)

FutureBuilder trong Flutter là gì

FutureBuilder là một widget Flutter tích hợp từ gói widgets, nhận Future và một hàm builder. Khi trạng thái của Future thay đổi (đang chạy, hoàn thành với dữ liệu, hoàn thành với lỗi), FutureBuilder tự động xây dựng lại UI bằng cách gọi builder với AsyncSnapshot mới. Điều này loại bỏ nhu cầu quản lý trạng thái tải thủ công qua setState và cờ.

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.

FutureBuilder hoạt động bên trong như thế nào

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: trạng thái và thuộc tính

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ínhLoạiMô tả
connectionStateConnectionStateTrạng thái kết nối hiện tại (none, waiting, active, done)
dataT?Dữ liệu nhận được từ Future (null cho đến khi hoàn thành hoặc khi lỗi)
errorObject?Đối tượng lỗi nếu Future hoàn thành với ngoại lệ
hasDatabooltrue nếu data không null và connectionState là ConnectionState.done
hasErrorbooltrue nếu Future hoàn thành với lỗi

ConnectionState: bốn trạng thái của thao tác bất đồng bộ

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.

Mẫu sử dụng FutureBuilder

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.

Tải dữ liệu khi khởi tạo màn hình

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.

Tải với bộ nhớ đệm và làm mới

Để 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 vs setState: chọn cái nào

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.

Ví dụ FutureBuilder tải dữ liệu mạng

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.

dart
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

Tại sao FutureBuilder gọi builder nhiều lần?

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àm thế nào để tránh yêu cầu lặp lại khi xây dựng lạ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 khác StreamBuilder như thế nào?

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.

Làm thế nào để sử dụng FutureBuilder với nhiều Future?

Đố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ộ.

Làm thế nào để hủy Future khi rời khỏi màn hình?

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

  • FutureBuilder — widget Flutter để xây dựng UI khai báo dựa trên trạng thái Future qua AsyncSnapshot (waiting, done, error)
  • AsyncSnapshot — vùng chứa với connectionState, data và error; cần thiết để xử lý đúng tất cả trạng thái thao tác bất đồng bộ
  • builder — callback với ba nhánh: hasError (hiển thị lỗi), hasData (hiển thị dữ liệu), default (chỉ báo tải)
  • FutureBuilder vs setState — FutureBuilder đơn giản hơn cho một thao tác, setState với Bloc/Riverpod tốt hơn cho logic phức tạp với nhiều yêu cầu
  • Ngăn yêu cầu lặp lại — Future nên là trường State, không tạo nó trong phương thức build để tránh khởi động lại mỗi lần xây dựng lại
  • Hủy Future — FutureBuilder không hủy Future khi dispose; sử dụng CancelableOperation hoặc cờ hủy để ngăn setState sau khi hủy
  • Nhiều Future — cho yêu cầu song song sử dụng Future.wait với một FutureBuilder; cho tuần tự — chuỗi trong một Future duy nhấ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.

Thảo luận dự án

Đọc thêm