InheritedWidget — khái niệm, truyền dữ liệu trong cây và cách hoạt động

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

InheritedWidget là một widget đặc biệt trong Flutter truyền dữ liệu xuống dưới trong cây widget mà không cần truyền tường minh qua các hàm tạo. Widget con truy cập dữ liệu thông qua BuildContext và tự động đăng ký nhận cập nhật. Khi dữ liệu trong InheritedWidget thay đổi, tất cả widget phụ thuộc được xây dựng lại. Theo Flutter API Reference, 2025, InheritedWidget là nền tảng của Theme, MediaQuery, Localizations và hầu hết các thư viện quản lý trạng thái.

Những điểm chính

  • InheritedWidget truyền dữ liệu xuống dưới trong Widget Tree mà không cần truyền tường minh qua từng widget.
  • Đăng ký tự động — widget sử dụng dependOnInheritedWidgetOfExactType được xây dựng lại khi dữ liệu thay đổi.
  • Theme và MediaQuery là các ví dụ tích hợp của InheritedWidget, có sẵn trong mọi ứng dụng Flutter.
  • Provider và Riverpod được xây dựng trên InheritedWidget và mở rộng khả năng quản lý trạng thái.
  • Triển khai đúng yêu cầu ghi đè updateShouldNotify để ngăn chặn việc xây dựng lại không cần thiết.

InheritedWidget trong Flutter là gì?

InheritedWidget là một widget làm cho dữ liệu của nó có sẵn cho tất cả các hậu duệ trong Widget Tree. Không giống như widget thông thường chỉ truyền dữ liệu qua hàm tạo cho các phần tử con, InheritedWidget cho phép bất kỳ widget nào trong cây con truy cập dữ liệu mà không cần chuỗi tham số. Điều này giải quyết vấn đề “prop drilling” — truyền dữ liệu qua nhiều widget trung gian không tự sử dụng dữ liệu này.

InheritedWidget tích hợp

Flutter bao gồm một số InheritedWidget tích hợp: Theme (bảng màu và kiểu), MediaQuery (kích thước màn hình, hướng, mật độ điểm ảnh), Localizations (chuỗi bản địa hóa), Directionality (hướng văn bản), DefaultTextStyle (kiểu văn bản mặc định). Các widget này được thiết lập bởi widget gốc như MaterialApp và có sẵn trong toàn bộ ứng dụng.

Vòng đời của InheritedWidget

InheritedWidget không có trạng thái riêng — nó lưu trữ dữ liệu được truyền qua hàm tạo. Khi cha của InheritedWidget được xây dựng lại với dữ liệu mới, phương thức updateShouldNotify được gọi để so sánh dữ liệu cũ và mới. Nếu phương thức trả về true, tất cả widget phụ thuộc được đánh dấu để xây dựng lại. Đây là cơ chế cập nhật phản ứng đơn giản nhưng hiệu quả.

Cách truyền dữ liệu qua InheritedWidget hoạt động

Cơ chế truyền dữ liệu qua InheritedWidget dựa trên Element Tree. Khi một widget gọi dependOnInheritedWidgetOfExactType, phần tử tương ứng đăng ký phụ thuộc vào InheritedElement. Khi InheritedWidget thay đổi, InheritedElement thông báo cho tất cả phần tử phụ thuộc, chúng được xây dựng lại trong khung hình tiếp theo.

Đăng ký phụ thuộc

Phương thức dependOnInheritedWidgetOfExactType không chỉ tìm InheritedWidget trong cây — nó đăng ký phần tử hiện tại nhận thông báo. Nếu bạn sử dụng findAncestorWidgetOfExactType thay vì dependOn, widget sẽ nhận được dữ liệu nhưng sẽ không được xây dựng lại khi dữ liệu thay đổi. Đây là sự khác biệt quan trọng: dependOn là đăng ký, findAncestor là tra cứu một lần.

Duyệt cây InheritedWidget

Khi một widget yêu cầu InheritedWidget, Flutter đi lên Element Tree từ phần tử hiện tại đến gốc, kiểm tra từng InheritedElement để tìm khớp kiểu. InheritedElement khớp đầu tiên được trả về. Điều này có nghĩa là InheritedWidget gần nhất trong cây có quyền ưu tiên — bạn có thể ghi đè dữ liệu ở một cấp cụ thể bằng cách đặt InheritedWidget gần hậu duệ hơn.

dart
class ThemeData {
  final Color primaryColor;
  final TextTheme textTheme;

  const ThemeData({required this.primaryColor, required this.textTheme});
}

class MyTheme extends InheritedWidget {
  final ThemeData data;

  const MyTheme({required this.data, required Widget child}) : super(child: child);

  static MyTheme of(BuildContext context) {
    final widget = context.dependOnInheritedWidgetOfExactType<MyTheme>();
    assert(widget != null, "MyTheme not found in tree");
    return widget!;
  }

  @override
  bool updateShouldNotify(MyTheme oldWidget) => oldWidget.data != data;
}

Trong ví dụ này, MyTheme sử dụng phương thức tĩnh of để cung cấp dữ liệu cho hậu duệ. Phương thức dependOnInheritedWidgetOfExactType đăng ký phụ thuộc và updateShouldNotify so sánh dữ liệu cũ và mới để xác định xem widget phụ thuộc có cần xây dựng lại hay không.

Tạo InheritedWidget tùy chỉnh

Tạo InheritedWidget tùy chỉnh gồm hai bước: định nghĩa một lớp mở rộng InheritedWidget và triển khai phương thức tĩnh of để hậu duệ truy cập. Dữ liệu được truyền qua hàm tạo và phương thức updateShouldNotify xác định khi nào widget phụ thuộc nên được xây dựng lại.

Bước 1: Định nghĩa lớp InheritedWidget

Lớp phải mở rộng InheritedWidget và chấp nhận dữ liệu qua hàm tạo với tham số child bắt buộc. Dữ liệu có thể thuộc bất kỳ kiểu nào: nguyên thủy, đối tượng, hàm. Quy tắc chính là dữ liệu phải bất biến (immutable) để có thể so sánh giá trị cũ và mới một cách đáng tin cậy.

Bước 2: Phương thức tĩnh of

Phương thức tĩnh of nhận BuildContext và trả về dữ liệu InheritedWidget. Bên trong, nó gọi dependOnInheritedWidgetOfExactType, tìm InheritedWidget gần nhất thuộc kiểu đã chỉ định trong cây. Nếu không tìm thấy InheritedWidget, phương thức ném ngoại lệ hoặc trả về giá trị mặc định tùy theo triển khai.

Bước 3: Sử dụng trong widget

Để truy cập dữ liệu, widget gọi MyWidget.of(context) bên trong phương thức build. Flutter tự động đăng ký widget nhận cập nhật. Nếu dữ liệu thay đổi, widget được xây dựng lại trong khung hình tiếp theo. Điều này cho phép mã sạch và khai báo mà không có tham số không cần thiết.

dart
class UserPreferences extends InheritedWidget {
  final String languageCode;
  final bool darkMode;

  const UserPreferences({
    required this.languageCode,
    required this.darkMode,
    required Widget child,
  }) : super(child: child);

  static UserPreferences of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<UserPreferences>()!;
  }

  @override
  bool updateShouldNotify(UserPreferences oldWidget) =>
    oldWidget.languageCode != languageCode || oldWidget.darkMode != darkMode;
}

Trong ví dụ này, UserPreferences lưu trữ cài đặt người dùng. Phương thức updateShouldNotify so sánh từng trường riêng lẻ, ngăn chặn việc xây dựng lại không cần thiết khi chỉ một tham số thay đổi. Sử dụng cách tiếp cận tương tự cho InheritedWidget tùy chỉnh của bạn với nhiều trường.

Phương thức updateShouldNotify và ngăn chặn xây dựng lại không cần thiết

updateShouldNotify là phương thức chính của InheritedWidget xác định xem widget phụ thuộc có cần được thông báo về thay đổi dữ liệu hay không. Nếu phương thức trả về false, widget phụ thuộc không được xây dựng lại, ngay cả khi InheritedWidget tự nhận được phiên bản mới với cùng dữ liệu. Điều này cực kỳ quan trọng cho hiệu suất.

Triển khai updateShouldNotify đúng cách

Chỉ so sánh những trường thực sự thay đổi và ảnh hưởng đến hiển thị. Nếu InheritedWidget chứa 10 trường nhưng chỉ một trường ảnh hưởng đến UI, chỉ kiểm tra trường đó. Với tập hợp, sử dụng so sánh sâu hoặc cấu trúc dữ liệu bất biến. Không sử dụng == cho List hoặc Map vì chúng so sánh theo tham chiếu.

  • Kiểu nguyên thủy — sử dụng so sánh trực tiếp: oldWidget.value != value.
  • Đối tượng bất biến — sử dụng == đã ghi đè: oldWidget.data != data (nếu data ghi đè ==).
  • Tập hợp — sử dụng listEquals, mapEquals từ package:flutter/foundation.dart.

Lỗi trong triển khai updateShouldNotify

Lỗi phổ biến nhất là trả về true mà không so sánh. Điều này khiến tất cả widget phụ thuộc được xây dựng lại ở mỗi lần cập nhật cha, ngay cả khi dữ liệu không thay đổi. Lỗi thứ hai là trả về false khi dữ liệu đã thay đổi, dẫn đến UI lỗi thời. Lỗi thứ ba là so sánh phức tạp chạy mỗi khung hình và làm chậm hiệu suất.

InheritedWidget vs callback: nên chọn gì?

InheritedWidget và callback (truyền hàm qua hàm tạo) giải quyết các vấn đề khác nhau. InheritedWidget phù hợp cho dữ liệu cần thiết bởi nhiều widget ở các cấp khác nhau trong cây. Callback thuận tiện cho việc truyền sự kiện một chiều từ cha đến một con cụ thể hoặc ngược lại. Lựa chọn phụ thuộc vào kiến trúc ứng dụng và tần suất cập nhật.

Khi nào sử dụng InheritedWidget

Sử dụng InheritedWidget khi dữ liệu cần thiết cho nhiều widget ở các cấp lồng nhau khác nhau: chủ đề ứng dụng, cài đặt người dùng, thông tin thiết bị, dữ liệu phiên hiện tại. InheritedWidget đặc biệt hiệu quả cho dữ liệu “toàn cầu” hiếm khi thay đổi nhưng cần thiết ở các phần khác nhau của UI.

Khi nào sử dụng callback

Callback (hàm gọi lại) phù hợp để truyền sự kiện từ widget con lên cha: nhấn nút, chọn mục danh sách, gửi biểu mẫu. Callback chỉ rõ hành động mà con có thể thực hiện và không tạo phụ thuộc ẩn. Để truyền dữ liệu xuống dưới qua một số cấp nhỏ, sử dụng tham số hàm tạo cũng đơn giản hơn.

Tiêu chíInheritedWidgetCallback
HướngTừ trên xuống (cha → hậu duệ)Từ dưới lên (con → cha) hoặc trực tiếp
Phạm viToàn bộ cây conWidget cụ thể
Xây dựng lạiTự động khi dữ liệu thay đổiYêu cầu setState thủ công
Độ phức tạpTrung bình (cần lớp InheritedWidget)Thấp (chỉ một hàm)

InheritedWidget và thư viện quản lý trạng thái

ProviderRiverpod là các thư viện quản lý trạng thái phổ biến trong Flutter được xây dựng trên InheritedWidget. Chúng mở rộng khả năng: thêm hỗ trợ ChangeNotifier, tự động hủy khi tháo khỏi cây, khởi tạo lười biếng và cú pháp đơn giản hóa với kiểu generic.

Provider dựa trên InheritedWidget

Provider sử dụng InheritedWidget để truyền đối tượng thuộc bất kỳ kiểu nào xuống dưới trong cây. ChangeNotifierProvider theo dõi thay đổi qua ChangeNotifier và gọi updateShouldNotify khi notifyListeners được gọi. Điều này giải phóng nhà phát triển khỏi việc tạo InheritedWidget thủ công và triển khai updateShouldNotify.

So sánh với InheritedWidget trực tiếp

InheritedWidget trực tiếp cho nhiều kiểm soát hơn và không yêu cầu phụ thuộc bên ngoài. Provider cung cấp cơ sở hạ tầng sẵn có: Consumer, Selector, MultiProvider, ProxyProvider. Lựa chọn phụ thuộc vào độ phức tạp của ứng dụng. Cho dự án đơn giản, InheritedWidget trực tiếp là đủ; cho dự án lớn, Provider hoặc Riverpod giảm mã mẫu.

dart
// InheritedWidget trực tiếp
class UserProvider extends InheritedWidget {
  final UserData userData;
  const UserProvider({required this.userData, required Widget child}) : super(child: child);
  static UserData of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<UserProvider>()!.userData;
  @override
  bool updateShouldNotify(UserProvider old) => old.userData != userData;
}

// Tương đương Provider
return ChangeNotifierProvider<UserData>(
  create: (_) => UserData(),
  child: MyApp(),
);

Cả hai cách tiếp cận trong ví dụ giải quyết cùng một vấn đề — truyền UserData xuống dưới trong cây. Provider giảm khối lượng mã nhưng ẩn cơ chế InheritedWidget. InheritedWidget trực tiếp cho kiểm soát hoàn toàn và hiểu biết về những gì đang xảy ra, điều đặc biệt quan trọng khi học Flutter và gỡ lỗi các vấn đề xây dựng lại phức tạp.

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

InheritedWidget khác widget thông thường như thế nào?

InheritedWidget làm cho dữ liệu có sẵn cho tất cả hậu duệ qua BuildContext, trong khi widget thông thường chỉ truyền dữ liệu qua hàm tạo. InheritedWidget cũng đăng ký hậu duệ nhận cập nhật dữ liệu.

Widget phụ thuộc được xây dựng lại bao lâu một lần?

Widget phụ thuộc chỉ được xây dựng lại khi updateShouldNotify trả về true. Nếu phương thức được triển khai đúng, việc xây dựng lại chỉ xảy ra khi dữ liệu thực sự thay đổi, không phải ở mỗi lần xây dựng lại cha.

Có thể sử dụng nhiều InheritedWidget trong cùng một cây không?

Có, bạn có thể sử dụng bất kỳ số lượng InheritedWidget trong cùng một cây. Mỗi widget cung cấp dữ liệu thuộc một kiểu cụ thể và widget có thể lấy dữ liệu từ nhiều InheritedWidget cùng lúc.

Sự khác biệt giữa dependOnInheritedWidgetOfExactType và findAncestorWidgetOfExactType là gì?

dependOn đăng ký widget nhận cập nhật — khi dữ liệu thay đổi, widget được xây dựng lại. findAncestor thực hiện tra cứu một lần mà không đăng ký và widget sẽ không biết về thay đổi dữ liệu.

InheritedWidget có phù hợp cho quản lý trạng thái phức tạp không?

Cho trạng thái đơn giản (chủ đề, cài đặt) InheritedWidget là đủ. Cho trạng thái phức tạp với logic nghiệp vụ, sử dụng Provider, Riverpod hoặc BLoC — chúng được xây dựng trên InheritedWidget và thêm cơ sở hạ tầng cần thiết.

Tổng kết

  • InheritedWidget là widget đặc biệt của Flutter để truyền dữ liệu xuống dưới trong cây với đăng ký tự động cập nhật.
  • Cơ chế hoạt động dựa trên Element Tree: InheritedElement đăng ký phần tử phụ thuộc và thông báo chúng về thay đổi.
  • updateShouldNotify — phương thức chính để ngăn chặn xây dựng lại không cần thiết của widget phụ thuộc.
  • InheritedWidget tích hợp: Theme, MediaQuery, Localizations, Directionality, DefaultTextStyle.
  • Tạo tùy chỉnh bao gồm kế thừa lớp, truyền dữ liệu qua hàm tạo và phương thức tĩnh of.
  • Provider và Riverpod được xây dựng trên InheritedWidget và thêm ChangeNotifier, Consumer, Selector và cú pháp đơn giản hóa.
  • InheritedWidget giải quyết vấn đề prop drilling và là nền tảng của quản lý trạng thái phản ứng trong 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