Provider: nó là gì, trình quản lý trạng thái ứng dụng Flutter

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

Provider — một gói quản lý trạng thái cho Flutter, được tạo bởi Remi Rousselet vào năm 2019 như một lớp bọc trên InheritedWidget. Provider giải quyết vấn đề truyền dữ liệu xuống cây widget mà không cần props drilling: bất kỳ widget nào cũng có thể truy cập trạng thái qua context.read<T>() hoặc context.watch<T>(). Theo pub.dev, Provider là trình quản lý trạng thái Flutter phổ biến nhất với hơn 25 nghìn lượt thích.

Điểm chính

  • ChangeNotifier — một lớp với notifyListeners để thông báo cho người đăng ký về các thay đổi
  • Consumer — widget tái xây dựng khi ChangeNotifier thay đổi
  • MultiProvider — tổ hợp nhiều Provider mà không lồng nhau
  • ProxyProvider — Provider phụ thuộc vào Provider khác
  • context.read / context.watch — phương thức đọc trạng thái không và có đăng ký

Provider là gì?

Provider — một gói quản lý trạng thái và tiêm phụ thuộc trong Flutter, được xây dựng trên InheritedWidget. Provider cung cấp một đối tượng (trạng thái, dịch vụ, kho lưu trữ) trong cây widget và tự động tái xây dựng UI khi dữ liệu thay đổi. Không giống như sử dụng trực tiếp InheritedWidget, Provider loại bỏ tất cả boilerplate: không cần viết lớp con của InheritedWidget, thiết lập phương thức tĩnh of() hoặc quản lý sự lồng ghép.

Provider là phương thức được Google khuyến nghị chính thức để quản lý trạng thái trong Flutter (Flutter Team, 2019-2023). Gói này là một phần của Hệ sinh thái Flutter và được duy trì bởi nhóm Flutter. Tại thời điểm ra mắt, Provider được đề xuất như một sự thay thế cho các biến toàn cục và InheritedWidget: bất kỳ đối tượng nào cũng có thể truy cập từ bất kỳ đâu mà không cần truyền qua hàm tạo.

Theo Khảo sát Cộng đồng Flutter 2025, Provider được sử dụng trong 72% ứng dụng Flutter. Các lý do chính cho sự phổ biến của nó là: ngưỡng đầu vào tối thiểu, hỗ trợ ChangeNotifier tích hợp sẵn, tương thích với các kiến trúc khác (MVVM, BLoC) và không có phụ thuộc bên ngoài.

ChangeNotifier và notifyListeners

ChangeNotifier — một lớp tích hợp sẵn của Flutter thực hiện mẫu Listener. ChangeNotifier thông báo cho người đăng ký về các thay đổi bằng cách gọi notifyListeners(). Trong bối cảnh của Provider, ChangeNotifier là lớp chính cho trạng thái: một lớp mở rộng ChangeNotifier được tạo với các trường và phương thức gọi notifyListeners() sau khi thay đổi dữ liệu.

Dart
class CounterProvider extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void reset() {
    _count = 0;
    notifyListeners();
  }
}

Quy tắc notifyListeners: gọi sau khi thay đổi dữ liệu hoàn toàn — không phải ở giữa phương thức mà ở cuối. Nếu một phương thức thực hiện nhiều thay đổi, hãy gọi notifyListeners() một lần sau tất cả các thay đổi, không phải sau mỗi lần. Điều này ngăn chặn nhiều lần vẽ lại trong một bước logic. Đối với các cập nhật hàng loạt, hãy sử dụng notifyListeners cùng với các mẫu tương tự setState.

Các lựa chọn thay thế cho ChangeNotifier: ValueNotifier — cho một giá trị duy nhất (tốt cho kiểu nguyên thủy), StateNotifier — từ gói state_notifier (ít khi được sử dụng một mình). Hầu hết các giải pháp Provider sử dụng ChangeNotifier do hỗ trợ tích hợp sẵn và sự đơn giản.

Consumer và context.watch

Consumer — một widget đăng ký vào ChangeNotifier và tái xây dựng mỗi khi notifyListeners() được gọi. Consumer chấp nhận một hàm builder với ba tham số: context, model, child. Child — một widget không phụ thuộc vào mô hình và Consumer không tái xây dựng. Đây là một tối ưu hóa: nếu Consumer chứa một widget tĩnh (biểu tượng, văn bản không có dữ liệu), nó được truyền qua child và không bị tạo lại.

Dart
Consumer<CounterProvider>(
  builder: (context, provider, child) => Column(
    children: [
      child!, // không được tái xây dựng
      Text('${provider.count}'),
      ElevatedButton(
        onPressed: () => provider.increment(),
        child: Icon(Icons.add),
      ),
    ],
  ),
  child: Text('Bộ đếm:'),
)

context.watch — một phương thức mở rộng của BuildContext để đăng ký vào Provider. Trả về mô hình và đăng ký widget hiện tại vào các thay đổi của nó. context.read — truy cập không có đăng ký (cho các trình xử lý onPressed, initState và dispose). context.select — đăng ký vào một trường cụ thể của mô hình mà không tái xây dựng khi các trường khác thay đổi. Select là tùy chọn hiệu quả nhất cho các mô hình phức tạp có 10+ trường.

Khi nào sử dụng Consumer, watch hoặc select: Consumer — khi cần một widget child để tối ưu hóa. watch — trong phương thức build để đọc đơn giản. select — khi mô hình có nhiều trường nhưng widget chỉ phụ thuộc vào một. Provider tự động hủy đăng ký khi widget bị hủy, ngăn chặn rò rỉ bộ nhớ.

MultiProvider và ProxyProvider

MultiProvider — một widget để đăng ký nhiều Provider mà không lồng nhau. Thay vì một cây với 5 cấp Provider → Provider → Provider, MultiProvider chấp nhận một danh sách các nhà cung cấp. Mỗi Provider tiếp theo có thể sử dụng các Provider trước đó qua hàm tạo. MultiProvider là cách tiêu chuẩn để tổ chức cấp gốc của một ứng dụng.

Dart
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => CartProvider()),
    ChangeNotifierProvider(create: (_) => AuthProvider()),
    ProxyProvider<AuthProvider, OrderProvider>(
      update: (_, auth, __) => OrderProvider(auth.userId),
    ),
  ],
  child: MaterialApp(home: HomePage()),
)

ProxyProvider — một Provider phụ thuộc vào Provider khác. ProxyProvider lấy giá trị từ các Provider khác và truyền chúng cho đối tượng của nó. Ví dụ, OrderProvider phụ thuộc vào AuthProvider (cần userId). Khi AuthProvider thay đổi, ProxyProvider tự động tạo lại OrderProvider với userId mới. ChangeNotifierProxyProvider — phiên bản ProxyProvider cho ChangeNotifier.

StreamProvider và FutureProvider: StreamProvider đăng ký vào một Stream (Firebase, WebSocket) và cập nhật Consumer tại mỗi sự kiện mới. FutureProvider — cho khởi tạo bất đồng bộ: chạy một Future, hiển thị tải, sau đó truyền kết quả cho các widget. Cả hai giải quyết các tác vụ phổ biến mà không cần quản lý đăng ký thủ công.

Kiểm thử Provider

Provider được kiểm thử bằng cách bọc widget trong một MultiProvider với các giá trị kiểm thử. Không cần API hoặc cơ sở dữ liệu thực cho kiểm thử — Provider được thay thế bằng một đối tượng giả lập. Gói provider cung cấp ProviderScope để cô lập kiểm thử — mỗi kiểm thử tạo cây Provider riêng của nó một cách độc lập.

Dart
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Counter increments on button tap',
    (tester) async {
      await tester.pumpWidget(
        ChangeNotifierProvider(
          create: (_) => CounterProvider(),
          child: CounterScreen(),
        ),
      );
      await tester.tap(find.byKey(Key('increment')));
      await tester.pump();
      expect(find.text('1'), findsOneWidget);
    },
  );
}

MockProvider: để kiểm thử các widget với Provider phụ thuộc vào API, hãy tạo một lớp con giả hoặc sử dụng mockito / mocktail. Provider không yêu cầu công cụ giả lập đặc biệt — bất kỳ đối tượng nào mở rộng ChangeNotifier đều có thể được truyền qua create mà không cần gọi dịch vụ thực. Lập trình Provider thông qua các giao diện (lớp trừu tượng) để dễ dàng thay thế.

Provider: hiệu suất và hạn chế

Hiệu suất Provider dựa trên InheritedWidget: khi một Provider thay đổi, tất cả các widget đăng ký qua context.watch hoặc Consumer đều được tái xây dựng. Để ngăn chặn việc vẽ lại không cần thiết, hãy sử dụng context.select (đăng ký vào một trường cụ thể), Consumer với tham số childconst cho các widget tĩnh. Provider không tái xây dựng các nhánh không đăng ký vào các thay đổi.

Phương thứcĐăng kýTái xây dựngSử dụng
context.watchToàn bộ mô hìnhBất kỳ thay đổiWidget đơn giản
ConsumerToàn bộ mô hìnhBất kỳ thay đổiVới tối ưu child
context.selectTrường cụ thểChỉ khi trường thay đổiMô hình phức tạp
context.readKhôngKhông bao giờTrình xử lý sự kiện

Hạn chế: Provider không hỗ trợ cô lập logic nghiệp vụ ở cấp độ sự kiện (như BLoC). Tất cả các thay đổi xảy ra thông qua các lời gọi trực tiếp đến các phương thức ChangeNotifier, có thể dẫn đến các chuỗi thay đổi không kiểm soát. Đối với các kịch bản phức tạp (nhiều hoạt động bất đồng bộ, xác thực phức tạp), Provider thua kém BLoC và Riverpod.

Di chuyển từ Provider: Provider có thể dễ dàng kết hợp với các gói khác. Để di chuyển sang Riverpod, hãy sử dụng ChangeNotifierProvider.adaptive — một bộ chuyển đổi cho phép sử dụng ChangeNotifier hiện có với Riverpod mà không cần viết lại. Đối với BLoC — BlocProvider có thể được đặt trong cây Provider, dần dần thay thế ChangeNotifier bằng Bloc.

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

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

Provider — một lớp bọc trên InheritedWidget để tiêm phụ thuộc với ChangeNotifier. BLoC — một mẫu kiến trúc với Event + Stream để cô lập logic. Provider dễ học hơn, BLoC cấu trúc mã chặt chẽ hơn. Provider phù hợp cho các ứng dụng nhỏ và trạng thái UI, BLoC cho logic nghiệp vụ phức tạp. Theo Flutter Community 2025, cả hai thường được sử dụng cùng nhau trong cùng một dự án.

ChangeNotifierProvider là gì?

ChangeNotifierProvider — một loại Provider cho các thể hiện ChangeNotifier. Tạo đối tượng qua create, cung cấp nó cho các widget con và tái xây dựng Consumer khi notifyListeners được gọi. ChangeNotifierProvider tự động gọi dispose trên ChangeNotifier khi bị xóa khỏi cây. Có ba phương thức tạo: ChangeNotifierProvider.value (cho đối tượng hiện có), ChangeNotifierProvider (cho tạo lười) và ChangeNotifierProvider.create (cho tạo lười rõ ràng).

Làm thế nào để tránh tái xây dựng không cần thiết với Provider?

Sử dụng context.select thay vì context.watch — widget chỉ tái xây dựng khi trường được chọn thay đổi. Chia các ChangeNotifier lớn thành nhiều cái nhỏ (một mô hình — một trách nhiệm). Sử dụng Consumer child cho các phần tĩnh. Đối với danh sách, hãy sử dụng ListView.builder với các khóa. Provider DevTools (Flutter Inspector) hiển thị widget nào đang tái xây dựng và tại sao.

Có thể sử dụng Provider mà không cần ChangeNotifier không?

Có. Provider (không có ChangeNotifier) — để tiêm các đối tượng bất biến (kho lưu trữ, trình khách API, cấu hình). ValueListenableProvider — cho ValueNotifier. StreamProvider — cho Stream (Firebase, WebSocket). FutureProvider — cho Future (tải cấu hình khi khởi động). ProxyProvider — cho các Provider phụ thuộc vào Provider khác. ChangeNotifier chỉ cần cho trạng thái có thể thay đổi với cập nhật UI.

ProviderNotFoundException là gì?

ProviderNotFoundException — một ngoại lệ thời gian chạy xảy ra khi cố gắng lấy một Provider không được khai báo ở trên trong cây widget. Nguyên nhân phổ biến: Provider được khai báo thấp hơn widget đang cố đọc nó; Provider được khai báo trong một route và được đọc trong một route khác; lỗi chính tả trong kiểu. Giải pháp: di chuyển Provider lên cao hơn trong cây hoặc sử dụng MultiProvider ở cấp MaterialApp cho các phụ thuộc toàn cục.

Tổng kết

  • Provider — một lớp bọc trên InheritedWidget để quản lý trạng thái và DI trong Flutter
  • ChangeNotifier — một lớp với notifyListeners cho các cập nhật UI phản ứng
  • Consumer và context.watch — đăng ký thay đổi với tái xây dựng tự động
  • MultiProvider — tổ hợp các nhà cung cấp mà không lồng cây
  • ProxyProvider — chuỗi phụ thuộc giữa các Provider
  • context.select — đăng ký mục tiêu vào một trường cụ thể của mô hình
  • StreamProvider và FutureProvider — hỗ trợ tích hợp sẵn cho dữ liệu phản ứng và bất đồng bộ

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