BLoC — nó là gì, Business Logic Component trong Flutter

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

BLoC (Business Logic Component) — một mẫu quản lý trạng thái cho Flutter, được Google giới thiệu vào năm 2018 tại DartConf. BLoC tách biệt logic kinh doanh khỏi giao diện người dùng thông qua các luồng phản ứng (Stream): UI gửi một Event, BLoC xử lý nó và trả về một State mới qua Stream. Theo pub.dev, gói flutter_bloc đã nhận được hơn 11 nghìn lượt thích và được sử dụng trong hàng nghìn ứng dụng Flutter.

Những điểm chính

  • Event — tín hiệu đầu vào mô tả một hành động: nhấn nút, tải dữ liệu
  • State — trạng thái đầu ra của UI: dữ liệu đã tải, lỗi, đang tải
  • Bloc — lớp chính nhận Event và trả về State qua Stream
  • Cubit — phiên bản đơn giản hóa của Bloc không có Event, gọi hàm trực tiếp
  • BlocProvider — widget Flutter để tiêm Bloc vào cây widget

BLoC là gì?

BLoC (Business Logic Component) — một mẫu kiến trúc cho Flutter trong đó logic kinh doanh được trích xuất vào một lớp riêng biệt, cách ly khỏi UI. BLoC nhận dữ liệu đầu vào qua một luồng sự kiện (Event) và tạo dữ liệu đầu ra qua một luồng trạng thái (State). Lớp trình bày (Widget) chỉ đăng ký luồng State và hiển thị UI, không bao giờ thực thi logic kinh doanh trực tiếp.

Khái niệm BLoC dựa trên lập trình phản ứng và mẫu Observer. Mỗi thành phần BLoC là một mô-đun riêng biệt với một hợp đồng rõ ràng: một tập hợp Event đã biết (điều gì có thể xảy ra) và một tập hợp State đã biết (điều gì có thể hiển thị). Nhà phát triển không thể "vô tình" thay đổi trạng thái từ UI — chỉ thông qua một Event cụ thể. Điều này làm cho mã có thể dự đoán và kiểm thử được.

Theo khảo sát Flutter Community 2025, BLoC đứng thứ hai về mức độ phổ biến trong các giải pháp quản lý trạng thái trong Flutter sau Provider. Ưu điểm chính: kiểu mạnh, cách ly logic, hỗ trợ Stream tích hợp, hệ sinh thái tiện ích phong phú (BlocProvider, BlocListener, BlocSelector).

Kiến trúc BLoC: Event → Bloc → State

Kiến trúc BLoC được xây dựng xung quanh ba thực thể: Event (đầu vào), Bloc (trình xử lý) và State (đầu ra). Widget gửi một Event qua phương thức add(). Bloc nhận Event trong phương thức mapEventToState hoặc on<Event>, thực thi logic kinh doanh và phát ra một State mới qua yield. Widget nhận State qua Stream và xây dựng lại.

Dart
abstract class CounterEvent {}

class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0);

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    if (event is Increment) {
      yield state + 1;
    } else if (event is Decrement) {
      yield state - 1;
    }
  }
}

An toàn kiểu: Bloc được tham số hóa với hai kiểu — Event và State. Trình biên dịch Dart kiểm tra rằng Widget chỉ gọi các Event đã khai báo và Bloc chỉ trả về các State đã khai báo. Lỗi thời gian chạy như "Hành động không xác định" được loại bỏ.

Close và Dispose: Bloc triển khai giao diện Closeable. Khi một widget bị hủy, Bloc tự động đóng Stream qua phương thức close(). Rò rỉ đăng ký phản ứng là không thể — BlocProvider quản lý vòng đời của Bloc, liên kết nó với một route hoặc trang.

Bloc và Cubit: so sánh

Cubit là một triển khai đơn giản hóa của Bloc không có Event, được giới thiệu trong gói flutter_bloc 6.0. Cubit khai báo phương thức trực tiếp thay vì các lớp Event: increment(), fetchData(). Bên trong, Cubit sử dụng cùng cơ chế dựa trên Stream nhưng ẩn lớp Event. Điều này giảm boilerplate 40-50% cho các kịch bản đơn giản.

Đặc điểmBlocCubit
Lớp EventBắt buộcKhông cần
BoilerplateCaoThấp
Theo dõi hành độngQua kiểu EventChỉ tên phương thức
Phù hợp choKịch bản phức tạpTrạng thái đơn giản
Phân tíchTự động qua EventThủ công

Khi nào chọn Cubit: trạng thái có 2-3 biến thể (loading, loaded, error), biểu mẫu đơn giản, bộ đếm, trạng thái UI (mở/đóng). Khi nào chọn Bloc: logic kinh doanh phức tạp với nhiều hành động: xử lý đơn hàng, ủy quyền, đồng bộ dữ liệu. Bloc cung cấp theo dõi chi tiết từng hành động qua Event — mọi lệnh gọi được ghi lại trong BlocObserver.

BlocObserver — một quan sát viên toàn cục theo dõi tất cả Bloc và Cubit trong ứng dụng. Nó cho phép ghi lại Event, State, lỗi và chuyển đổi. Chỉ cần kết nối một phiên bản: Bloc.observer = AppBlocObserver(), và toàn bộ theo dõi trạng thái ứng dụng có sẵn tập trung.

BlocProvider và BlocBuilder

BlocProvider — một InheritedWidget từ flutter_bloc cung cấp Bloc cho các widget con. Khi một widget được khởi tạo, BlocProvider tạo một Bloc, và khi bị hủy — tự động đóng nó qua close(). BlocProvider có thể được đặt ở cấp MaterialApp (Bloc toàn cục) hoặc ở cấp route cụ thể (Bloc cục bộ).

Dart
BlocProvider(
  create: (context) => CounterBloc(),
  child: Column(
    children: [
      BlocBuilder<CounterBloc, int>(
        builder: (context, state) => Text('$state'),
      ),
      ElevatedButton(
        onPressed: () => context.read<CounterBloc>().add(Increment()),
        child: Text('+'),
      ),
    ],
  ),
)

BlocBuilder — một widget xây dựng lại UI ở mỗi State mới. BlocListener — cho các tác dụng phụ (xử lý State một lần, không xây dựng lại UI): hiển thị SnackBar, điều hướng đến màn hình khác. BlocConsumer — sự kết hợp của Builder và Listener cho các trường hợp cần cả xây dựng lại và tác dụng phụ. BlocSelector — để xây dựng lại có chọn lọc chỉ khi một trường cụ thể của State thay đổi.

MultiBlocProvider — một widget cho các BlocProvider lồng nhau mà không tăng cấp độ lồng. Một ứng dụng Flutter với 10-15 Bloc sử dụng MultiBlocProvider ở cấp gốc để đăng ký tất cả Bloc có sẵn cho toàn bộ ứng dụng: AuthenticationBloc, CartBloc, SettingsBloc.

Kiểm thử BLoC

BLoC được kiểm thử một cách cách ly không có widget Flutter. Chỉ cần nhập gói Dart flutter_test và gói bloc_test. Kịch bản kiểm thử: tạo Bloc, thêm Event, kiểm tra State. blocTest — một tiện ích tự động hóa chuỗi: build → act → expect.

Dart
blocTest<CounterBloc, int>(
  'emits [1] when Increment is added',
  build: () => CounterBloc(),
  act: (bloc) => bloc.add(Increment()),
  expect: () => [1],
)

Mocking: Một Bloc phụ thuộc vào kho lưu trữ hoặc API được kiểm thử với mock qua mocktail. Kho lưu trữ được mock ở cấp độ trừu tượng, và Bloc nhận các phụ thuộc được mock qua hàm tạo. Hydrated Bloc — một phần mở rộng cho việc lưu trữ/khôi phục trạng thái tự động trong bộ nhớ cục bộ. Nó được kiểm thử với HydratedBlocStorage và bộ nhớ tệp tạm thời.

BLoC trong sản xuất

Thư mục và tệp: một cấu trúc dự án Flutter điển hình với BLoC: bloc/counter_bloc.dart, bloc/counter_event.dart, bloc/counter_state.dart. Cho 30+ màn hình, nhóm theo tính năng được khuyến nghị: features/auth/bloc/, features/cart/bloc/. Mỗi Bloc là một tệp riêng, mỗi Event và State — trong các tệp riêng hoặc trong một tệp với Bloc.

Hiệu suất: BLoC không tạo ra chi phí cho Stream rỗng. BlocBuilder sử dụng buildWhen để lọc việc xây dựng lại — widget chỉ cập nhật khi một điều kiện cụ thể thay đổi. Close đảm bảo rằng các Bloc không hoạt động không tiêu tốn bộ nhớ. Theo Flutter DevTools, BLoC thêm ít hơn 1% vào kích thước bundle.

Di chuyển từ Provider: BLoC dễ dàng cùng tồn tại với Provider trong cùng một dự án. Di chuyển dần dần: đầu tiên thay thế các Provider phức tạp nhất bằng Bloc, sau đó là phần còn lại. BlocProvider tương thích với cây Provider: widget cũ có thể sử dụng Provider, widget mới — BlocProvider, trong cùng một ứng dụng.

Câu hỏi thường gặp

BLoC khác Provider trong Flutter như thế nào?

BLoC sử dụng Event + Stream để cách ly logic kinh doanh và kiểu mạnh. Provider là một lớp bọc quanh InheritedWidget để tiêm phụ thuộc đơn giản và ChangeNotifier. BLoC phù hợp hơn cho các kịch bản phức tạp với nhiều trạng thái, Provider — cho trạng thái UI cục bộ. BLoC yêu cầu nhiều boilerplate hơn nhưng cung cấp khả năng truy xuất đầy đủ qua Event.

Hydrated Bloc là gì?

Hydrated Bloc là một phần mở rộng từ gói hydrated_bloc, tự động lưu State cuối cùng vào bộ nhớ cục bộ (Hive theo mặc định). Khi ứng dụng khởi động lại, Bloc khôi phục trạng thái đã lưu thay vì trạng thái ban đầu. Điều này giải quyết vấn đề duy trì mà không cần gọi lưu thủ công: đăng nhập, giỏ hàng, cài đặt được lưu tự động giữa các phiên.

Làm thế nào để xử lý lỗi trong BLoC?

Một lỗi trong BLoC được xử lý thông qua try-catch bên trong mapEventToState hoặc on<Event>. Khi có lỗi, Bloc trả về State lỗi: yield LoadError(error.message). Trên UI, BlocListener hoặc BlocConsumer kiểm tra State theo loại lỗi và hiển thị SnackBar hoặc hộp thoại. BlocObserver ghi lại tất cả các ngoại lệ chưa được xử lý trên toàn cục.

Có thể sử dụng BLoC với các framework khác không?

BLoC là một mẫu dành riêng cho Flutter vì nó sử dụng Dart Stream và widget Flutter. Khái niệm Event → Bloc → State có thể được điều chỉnh cho AngularDartServer-side Dart, nhưng hệ sinh thái chính (BlocProvider, BlocBuilder, BlocObserver) phụ thuộc vào Flutter. Cho React Native, hãy sử dụng Redux hoặc MobX; cho SwiftUI, hãy sử dụng Combine + MVVM.

Nên chọn gì: Bloc hay Cubit?

Cubit — cho trạng thái đơn giản (bộ đếm, công tắc, biểu mẫu với 2-3 trường). Bloc — cho logic phức tạp (nguồn cấp tin tức, xử lý đơn hàng, ủy quyền). Nguyên tắc chính: nếu bạn cần theo dõi từng hành động (Event) để phân tích hoặc gỡ lỗi — hãy chọn Bloc. Nếu các phương thức thay đổi trạng thái là đủ — hãy chọn Cubit. Cả hai mẫu có thể cùng tồn tại trong cùng một dự án.

Tóm tắt

  • BLoC — mẫu quản lý trạng thái Flutter qua Event → Stream → State
  • Event — hành động (nhấn, tải), State — phản ứng (dữ liệu, lỗi, đang tải)
  • Cubit — phiên bản đơn giản hóa không có Event, giảm đến 50% boilerplate
  • BlocProvider — tiêm Bloc vào cây widget với tự động đóng
  • BlocObserver — giám sát toàn cục tất cả Bloc và Cubit trong ứng dụng
  • Hydrated Bloc — duy trì trạng thái tự động qua Hive
  • blocTest — tiện ích kiểm thử đơn vị Bloc cách ly khỏi Flutter

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