InheritedWidget — چیست، انتقال داده در درخت و چگونه کار می‌کند

نویسنده: IT Sectr منتشر شده: 2026-07-02 زمان مطالعه: 9 دقیقه

InheritedWidget — یک ویجت ویژه در Flutter است که داده‌ها را بدون ارسال صریح از طریق سازنده‌ها به پایین درخت ویجت منتقل می‌کند. ویجت‌های فرزند از طریق BuildContext به داده‌ها دسترسی پیدا می‌کنند و به طور خودکار در به‌روزرسانی‌ها مشترک می‌شوند. هنگامی که داده‌ها در InheritedWidget تغییر می‌کنند، همه ویجت‌های وابسته بازسازی می‌شوند. بر اساس Flutter API Reference, 2025، InheritedWidget در پایه Theme، MediaQuery، Localizations و اکثر کتابخانه‌های مدیریت وضعیت قرار دارد.

نکات اصلی

  • InheritedWidget داده‌ها را بدون ارسال صریح از طریق هر ویجت به پایین Widget Tree منتقل می‌کند.
  • اشتراک خودکار — ویجت‌هایی که از dependOnInheritedWidgetOfExactType استفاده می‌کنند، با تغییر داده‌ها بازسازی می‌شوند.
  • Theme و MediaQuery — نمونه‌های داخلی InheritedWidget که در هر برنامه Flutter در دسترس هستند.
  • Provider و Riverpod بر روی InheritedWidget ساخته شده‌اند و قابلیت‌های مدیریت وضعیت آن را گسترش می‌دهند.
  • پیاده‌سازی صحیح نیاز به بازنویسی updateShouldNotify برای جلوگیری از بازسازی‌های غیرضروری دارد.

InheritedWidget در Flutter چیست؟

InheritedWidget — ویجتی است که داده‌های خود را برای همه فرزندان در Widget Tree قابل دسترسی می‌کند. برخلاف ویجت معمولی که داده‌ها را فقط از طریق سازنده به عناصر فرزند منتقل می‌کند، InheritedWidget به هر ویجتی در زیردرخت اجازه می‌دهد بدون زنجیره پارامترها به داده‌ها دسترسی پیدا کند. این مشکل "prop drilling" را حل می‌کند — انتقال داده از طریق ویجت‌های میانی متعدد که خود از این داده‌ها استفاده نمی‌کنند.

InheritedWidgetهای داخلی

Flutter شامل چند InheritedWidget داخلی است: Theme (طرح رنگ و سبک‌ها)، MediaQuery (اندازه صفحه، جهت، تراکم پیکسل)، Localizations (رشته‌های بومی‌سازی شده)، Directionality (جهت متن)، DefaultTextStyle (سبک پیش‌فرض متن). این ویجت‌ها توسط ویجت‌های ریشه مانند MaterialApp تنظیم می‌شوند و در کل برنامه در دسترس هستند.

چرخه زندگی InheritedWidget

InheritedWidget وضعیت خاص خود را ندارد — داده‌های ارسال شده از طریق سازنده را ذخیره می‌کند. هنگامی که والد InheritedWidget با داده‌های جدید بازسازی می‌شود، متد updateShouldNotify برای مقایسه داده‌های قدیم و جدید فراخوانی می‌شود. اگر متد true برگرداند، همه ویجت‌های وابسته برای بازسازی علامت‌گذاری می‌شوند. این یک مکانیزم ساده اما مؤثر برای به‌روزرسانی واکنشی است.

انتقال داده از طریق InheritedWidget چگونه کار می‌کند

مکانیزم انتقال داده از طریق InheritedWidget بر اساس Element Tree است. هنگامی که یک ویجت dependOnInheritedWidgetOfExactType را فراخوانی می‌کند، عنصر مربوطه وابستگی به InheritedElement را ثبت می‌کند. هنگام تغییر InheritedWidget، InheritedElement همه عناصر وابسته را مطلع می‌کند که در فریم بعدی بازسازی می‌شوند.

ثبت وابستگی

متد dependOnInheritedWidgetOfExactType نه تنها InheritedWidget را در درخت پیدا می‌کند — بلکه عنصر جاری را برای اعلان‌ها مشترک می‌کند. اگر به جای dependOn از findAncestorWidgetOfExactType استفاده می‌کردید، ویجت داده‌ها را دریافت می‌کرد اما با تغییر آنها بازسازی نمی‌شد. این تفاوت مهمی است: dependOn اشتراک است، findAncestor جستجوی یکباره.

پیمایش درخت InheritedWidget

هنگامی که یک ویجت InheritedWidget را درخواست می‌کند، Flutter از عنصر جاری تا ریشه در Element Tree بالا می‌رود و هر InheritedElement را از نظر تطابق نوع بررسی می‌کند. اولین InheritedElement یافت شده برگردانده می‌شود. این بدان معناست که نزدیک‌ترین InheritedWidget در درخت اولویت دارد — می‌توانید داده‌ها را در سطح خاصی با قرار دادن InheritedWidget نزدیک‌تر به فرزندان بازنویسی کنید.

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;
}

در این مثال MyTheme از متد ایستای of برای ارائه داده به فرزندان استفاده می‌کند. متد dependOnInheritedWidgetOfExactType وابستگی را ثبت می‌کند و updateShouldNotify داده‌های قدیم و جدید را برای تعیین نیاز به بازسازی ویجت‌های وابسته مقایسه می‌کند.

ایجاد InheritedWidget سفارشی خود

ایجاد InheritedWidget سفارشی خود از دو مرحله تشکیل شده است: تعریف کلاسی که از InheritedWidget ارث می‌برد و پیاده‌سازی متد ایستای of برای دسترسی از فرزندان. داده‌ها از طریق سازنده منتقل می‌شوند و متد updateShouldNotify تعیین می‌کند که ویجت‌های وابسته چه زمانی باید بازسازی شوند.

مرحله 1: تعریف کلاس InheritedWidget

کلاس باید از InheritedWidget ارث برده و داده‌ها را از طریق سازنده با پارامتر اجباری child دریافت کند. داده‌ها می‌توانند از هر نوعی باشند: اولیه‌ها، اشیاء، توابع. قانون اصلی — داده‌ها باید غیرقابل تغییر (immutable) باشند تا بتوان به طور قابل اعتماد مقادیر قدیم و جدید را مقایسه کرد.

مرحله 2: متد ایستای of

متد ایستای of یک BuildContext دریافت می‌کند و داده‌های InheritedWidget را برمی‌گرداند. در داخل، dependOnInheritedWidgetOfExactType فراخوانی می‌شود که نزدیک‌ترین InheritedWidget از نوع مشخص شده را در درخت جستجو می‌کند. اگر InheritedWidget یافت نشود، متد بسته به پیاده‌سازی یا استثنا پرتاب می‌کند یا مقدار پیش‌فرض برمی‌گرداند.

مرحله 3: استفاده در ویجت‌ها

برای دسترسی به داده‌ها، ویجت MyWidget.of(context) را در داخل متد build فراخوانی می‌کند. Flutter به طور خودکار ویجت را برای به‌روزرسانی‌ها مشترک می‌کند. اگر داده‌ها تغییر کنند، ویجت در فریم بعدی بازسازی می‌شود. این امکان ایجاد کد تمیز و اعلامی بدون پارامترهای اضافی را فراهم می‌کند.

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;
}

در این مثال UserPreferences تنظیمات کاربر را ذخیره می‌کند. متد updateShouldNotify هر فیلد را جداگانه مقایسه می‌کند که از بازسازی‌های غیرضروری هنگام تغییر فقط یک پارامتر جلوگیری می‌کند. برای InheritedWidgetهای سفارشی خود با چندین فیلد از رویکرد مشابهی استفاده کنید.

متد updateShouldNotify و جلوگیری از بازسازی‌های غیرضروری

updateShouldNotify — متد کلیدی InheritedWidget است که تعیین می‌کند آیا باید ویجت‌های وابسته را از تغییر داده‌ها مطلع کرد. اگر متد false برگرداند، ویجت‌های وابسته حتی اگر خود InheritedWidget نمونه جدیدی با همان داده‌ها دریافت کرده باشد، بازسازی نمی‌شوند. این برای عملکرد حیاتی است.

پیاده‌سازی صحیح updateShouldNotify

فقط فیلدهایی را مقایسه کنید که واقعاً تغییر کرده‌اند و بر نمایش تأثیر می‌گذارند. اگر InheritedWidget شامل 10 فیلد است اما فقط یکی از آنها بر UI تأثیر می‌گذارد، فقط همان فیلد را بررسی کنید. برای مجموعه‌ها از مقایسه عمیق یا ساختارهای داده غیرقابل تغییر استفاده کنید. از == برای List یا Map استفاده نکنید، زیرا آنها با مرجع مقایسه می‌شوند.

  • اولیه‌ها — از مقایسه مستقیم استفاده کنید: oldWidget.value != value.
  • اشیاء غیرقابل تغییر — از == بازنویسی شده استفاده کنید: oldWidget.data != data (اگر data == را بازنویسی می‌کند).
  • مجموعه‌ها — از listEquals، mapEquals از package:flutter/foundation.dart استفاده کنید.

خطاها در پیاده‌سازی updateShouldNotify

رایج‌ترین خطا — برگرداندن true بدون مقایسه. این منجر به بازسازی همه ویجت‌های وابسته در هر به‌روزرسانی والد می‌شود، حتی اگر داده‌ها تغییر نکرده باشند. خطای دوم — برگرداندن false وقتی داده‌ها تغییر کرده‌اند که منجر به UI قدیمی می‌شود. سوم — مقایسه پیچیده که در هر فریم اجرا می‌شود و کار را کند می‌کند.

InheritedWidget در مقابل callbackها: کدام را انتخاب کنیم؟

InheritedWidget و callbackها (ارسال توابع از طریق سازنده) وظایف متفاوتی را حل می‌کنند. InheritedWidget برای داده‌هایی مناسب است که به ویجت‌های زیادی در سطوح مختلف درخت نیاز دارند. Callbackها برای انتقال یک‌طرفه رویدادها از والد به فرزند خاص یا برعکس مناسب هستند. انتخاب به معماری برنامه و دفعات تغییرات بستگی دارد.

چه زمانی از InheritedWidget استفاده کنیم

زمانی از InheritedWidget استفاده کنید که داده‌ها به ویجت‌های زیادی در سطوح مختلف تو در تو نیاز باشند: تم برنامه، تنظیمات کاربر، اطلاعات دستگاه، داده‌های جلسه جاری. InheritedWidget به ویژه برای داده‌های "سراسری" که به ندرت تغییر می‌کنند اما در بخش‌های مختلف UI مورد نیاز هستند، مؤثر است.

چه زمانی از callbackها استفاده کنیم

Callbackها (توابع بازگشتی) برای انتقال رویدادها از ویجت فرزند به والد مناسب هستند: کلیک دکمه، انتخاب آیتم لیست، ارسال فرم. Callbackها به صراحت نشان می‌دهند که فرزند چه اقداماتی می‌تواند انجام دهد و وابستگی‌های پنهان ایجاد نمی‌کنند. برای انتقال داده به پایین درخت در تعداد کمی سطح نیز استفاده از پارامترهای سازنده ساده‌تر است.

معیارInheritedWidgetCallbackها
جهتاز بالا به پایین (والد ← فرزندان)از پایین به بالا (فرزند ← والد) یا مستقیم
محدودهکل زیردرختویجت خاص
بازسازیخودکار با تغییر داده‌هانیاز به setState دستی
پیچیدگیمتوسط (نیاز به کلاس InheritedWidget)کم (یک تابع ساده)

InheritedWidget و کتابخانه‌های مدیریت وضعیت

Provider و Riverpod — کتابخانه‌های محبوب مدیریت وضعیت در Flutter هستند که بر روی InheritedWidget ساخته شده‌اند. آنها قابلیت‌های آن را گسترش می‌دهند: پشتیبانی از ChangeNotifier، حذف خودکار هنگام جدا شدن، مقداردهی اولیه تنبل و نحو ساده‌شده از طریق جنریک‌ها.

Provider مبتنی بر InheritedWidget

Provider از InheritedWidget برای انتقال یک شیء از هر نوع به پایین درخت استفاده می‌کند. ChangeNotifierProvider تغییرات را از طریق ChangeNotifier ردیابی می‌کند و با فراخوانی notifyListeners، updateShouldNotify را فراخوانی می‌کند. این کار توسعه‌دهنده را از ایجاد دستی InheritedWidget و پیاده‌سازی updateShouldNotify بی‌نیاز می‌کند.

مقایسه با InheritedWidget مستقیم

InheritedWidget مستقیم کنترل بیشتری می‌دهد و نیاز به وابستگی‌های خارجی ندارد. Provider زیرساخت آماده ارائه می‌دهد: Consumer، Selector، MultiProvider، ProxyProvider. انتخاب به پیچیدگی برنامه بستگی دارد. برای پروژه‌های ساده InheritedWidget مستقیم کافی است، برای پروژه‌های بزرگ Provider یا Riverpod کد الگویی را کاهش می‌دهند.

dart
// InheritedWidget مستقیم
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;
}

// معادل Provider
return ChangeNotifierProvider<UserData>(
  create: (_) => UserData(),
  child: MyApp(),
);

هر دو رویکرد در مثال یک وظیفه را حل می‌کنند — انتقال UserData به پایین درخت. Provider مقدار کد را کاهش می‌دهد اما مکانیک InheritedWidget را پنهان می‌کند. InheritedWidget مستقیم کنترل کامل و درک آنچه اتفاق می‌افتد را فراهم می‌کند که به ویژه در یادگیری Flutter و اشکال‌زدایی مشکلات پیچیده بازسازی مهم است.

سوالات متداول

InheritedWidget چه تفاوتی با ویجت معمولی دارد؟

InheritedWidget داده‌ها را از طریق BuildContext برای همه فرزندان قابل دسترسی می‌کند، در حالی که ویجت معمولی داده‌ها را فقط از طریق سازنده منتقل می‌کند. InheritedWidget همچنین فرزندان را برای به‌روزرسانی‌های داده مشترک می‌کند.

ویجت‌های وابسته چند وقت یکبار بازسازی می‌شوند؟

ویجت‌های وابسته فقط زمانی بازسازی می‌شوند که updateShouldNotify true برگرداند. اگر متد به درستی پیاده‌سازی شود، بازسازی فقط با تغییر واقعی داده‌ها اتفاق می‌افتد، نه با هر بازسازی والد.

آیا می‌توان از چند InheritedWidget در یک درخت استفاده کرد؟

بله، می‌توان از هر تعداد InheritedWidget در یک درخت استفاده کرد. هر کدام داده‌های نوع خاصی را ارائه می‌دهند و ویجت‌ها می‌توانند همزمان از چند InheritedWidget داده دریافت کنند.

تفاوت dependOnInheritedWidgetOfExactType با findAncestorWidgetOfExactType چیست؟

dependOn ویجت را برای به‌روزرسانی‌ها مشترک می‌کند — با تغییر داده‌ها ویجت بازسازی می‌شود. findAncestor یک جستجوی یکباره بدون اشتراک انجام می‌دهد و ویجت از تغییرات داده مطلع نخواهد شد.

آیا InheritedWidget برای مدیریت وضعیت پیچیده مناسب است؟

برای وضعیت ساده (تم، تنظیمات) InheritedWidget کافی است. برای وضعیت پیچیده با منطق تجاری از Provider، Riverpod یا BLoC استفاده کنید — آنها بر روی InheritedWidget ساخته شده‌اند و زیرساخت لازم را اضافه می‌کنند.

خلاصه

  • InheritedWidget — یک ویجت ویژه Flutter برای انتقال داده به پایین درخت با اشتراک خودکار در به‌روزرسانی‌ها.
  • مکانیزم کار مبتنی بر Element Tree است: InheritedElement عناصر وابسته را ثبت می‌کند و آنها را از تغییرات مطلع می‌سازد.
  • updateShouldNotify — متد کلیدی برای جلوگیری از بازسازی‌های غیرضروری ویجت‌های وابسته.
  • InheritedWidgetهای داخلی: Theme، MediaQuery، Localizations، Directionality، DefaultTextStyle.
  • ایجاد InheritedWidget سفارشی شامل ارث‌بری از کلاس، انتقال داده از طریق سازنده و متد ایستای of است.
  • Provider و Riverpod بر روی InheritedWidget ساخته شده‌اند و ChangeNotifier، Consumer، Selector و نحو ساده‌شده را اضافه می‌کنند.
  • InheritedWidget مشکل prop drilling را حل می‌کند و پایه مدیریت وضعیت واکنشی در Flutter است.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید