BuildContext — این چیست، مفاهیم کلیدی و اصل کار

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

BuildContext — شیء بنیادی Flutter است که موقعیت یک ویجت خاص را در درخت عناصر نشان می‌دهد و دسترسی به محیط آن را فراهم می‌کند. طبق مستندات رسمی Flutter (Flutter.dev, 2026)، BuildContext پلی بین ویجت و فریم‌ورک است: از طریق آن ویجت تم (Theme)، کوئری‌های رسانه (MediaQuery)، بومی‌سازی (Localizations) و داده‌های InheritedWidget را دریافت می‌کند. هر ویجت BuildContext مخصوص خود را دارد که به عنوان اولین آرگومان به متد build ارسال می‌شود.

نکات اصلی

  • BuildContext — شیئی که موقعیت ویجت را در درخت عناصر نشان می‌دهد و دسترسی به محیط سلسله‌مراتبی آن را فراهم می‌کند
  • InheritedWidget — مکانیزم اصلی انتقال داده به پایین درخت که از طریق BuildContext قابل دسترسی است
  • متد of() — متد استاتیکی که از BuildContext برای یافتن نزدیک‌ترین InheritedWidget به بالا در درخت استفاده می‌کند (Theme.of, MediaQuery.of)
  • کانتکست و چرخه حیات — BuildContext با جابجایی ویجت تغییر می‌کند; ارجاع به کانتکست پس از dispose قابل ذخیره نیست
  • خطاها — استفاده از BuildContext خارج از درخت آن یا پس از dispose منجر به استثنا می‌شود (بارگذاری مجدد داغ، callbackهای ناهمگام)

BuildContext چیست؟

BuildContext — رابطی است که توسط کلاس Element پیاده‌سازی می‌شود و به ویجت اطلاعاتی درباره مکان آن در سلسله‌مراتب UI می‌دهد. هر نمونه BuildContext برای موقعیت خاصی در درخت منحصربه‌فرد است و نمی‌تواند به جای دیگری منتقل شود. اگر ویجت والد خود را تغییر دهد (مثلاً به ظرف دیگری منتقل شود)، BuildContext جدیدی دریافت می‌کند.

هدف اصلی BuildContext دسترسی به InheritedWidget است. از طریق کانتکست، ویجت نزدیک‌ترین نمونه Theme، MediaQuery، Navigator یا Directionality را با بالا رفتن در درخت پیدا می‌کند. این مکانیزم اساس کل سیستم تم، ناوبری و چیدمان تطبیقی در Flutter است. بدون BuildContext هیچ ویجتی نمی‌تواند این داده‌ها را دریافت کند.

طبق مستندات معماری Flutter (Google, 2026)، BuildContext همچنین برای یافتن شیء RenderObject مرتبط با ویجت، اندازه‌گیری ابعاد و موقعیت‌یابی استفاده می‌شود. متدهایی مانند findRenderObject() و size دقیقاً از طریق کانتکست قابل دسترسی هستند. کانتکست همچنین از طریق Localizations.of(context) دسترسی به بومی‌سازی را فراهم می‌کند.

BuildContext عنصر است، نه ویجت

درک معماری مهم: BuildContext رابطی است که Element آن را پیاده‌سازی می‌کند، نه Widget. Element «چسب» بین Widget (پیکربندی) و RenderObject (نمایش واقعی) است. وقتی در مستندات «کانتکست ویجت» گفته می‌شود، منظور عنصری است که آن ویجت را مدیریت می‌کند. متد build دقیقاً چنین کانتکستی دریافت می‌کند — کانتکست ویجت در حال ایجاد، نه ویجت‌های فرزند برگشتی.

BuildContext چگونه کار می‌کند؟

مکانیزم کار BuildContext بر اساس پیمایش درخت عناصر از پایین به بالا است. وقتی ویجت Theme.of(context) را فرامی‌خواند، کانتکست جستجو را از عنصر جاری شروع کرده و به سمت ریشه حرکت می‌کند و هر عنصر را از نظر وجود InheritedWidget با نوع Theme بررسی می‌کند. اولین InheritedWidget یافت شده برگردانده می‌شود — این تضمین می‌کند که ویجت تم را از نزدیک‌ترین تعریف دریافت می‌کند.

هر BuildContext ارجاعی به کانتکست والد (parent) و کانتکست‌های فرزند ذخیره می‌کند. این یک ارتباط دوطرفه است که امکان حرکت در درخت را هم به بالا (به والدین) و هم به پایین (به فرزندان) فراهم می‌کند. در Flutter برای جستجوی InheritedWidget فقط از حرکت به بالا استفاده می‌شود — ویجت می‌تواند داده‌ها را فقط از اجداد دریافت کند، نه از نوادگان. این یک محدودیت معماری بنیادی است.

طبق کد منبع Flutter (Flutter SDK, 2026)، BuildContext شامل متدهای: visitAncestorElements, visitChildElements, findAncestorWidgetOfExactType, dependOnInheritedWidgetOfExactType و getRenderObject است. دو مورد آخر پرکاربردترین هستند: dependOnInheritedWidgetOfExactType نه تنها InheritedWidget را پیدا می‌کند، بلکه در تغییرات آن مشترک می‌شود (ویجت با تغییر InheritedWidget بازسازی خواهد شد).

اشتراک از طریق کانتکست

dependOnInheritedWidgetOfExactType — متد کلیدی BuildContext که واکنش‌گرایی را تضمین می‌کند. وقتی ویجت Theme.of(context) را فرامی‌خواند، نه تنها تم را دریافت می‌کند — در تغییرات آن مشترک می‌شود. اگر Theme تغییر کند (مثلاً هنگام تغییر تم تاریک/روشن)، همه ویجت‌های مشترک به طور خودکار بازسازی می‌شوند. این مکانیزم واکنش‌گرایی در Flutter است.

BuildContext در مقابل Element

BuildContext یک رابط است و Element — پیاده‌سازی آن. در کد Flutter شما همیشه از طریق رابط BuildContext کار می‌کنید، بدون اینکه نوع خاص عنصر (StatelessElement, StatefulElement, ProxyElement و غیره) را بدانید. این عمدی است: توسعه‌دهنده نیازی به دانستن جزئیات پیاده‌سازی عنصر ندارد — رابط برای دسترسی به محیط کافی است.

انواع مختلف عناصر BuildContext را متفاوت پیاده‌سازی می‌کنند: StatelessElement simplemente فراخوانی‌های build را منتقل می‌کند، StatefulElement State را مدیریت می‌کند و InheritedElement اشتراک‌ها را از طریق dependOnInheritedWidgetOfExactType ردیابی می‌کند. اما از دید توسعه‌دهنده همه آنها BuildContext با API یکسان هستند.

جنبهBuildContextElement
نوعرابط (abstract class)کلاس پیاده‌سازی
استفادهتوسط توسعه‌دهنده در buildمکانیزم داخلی Flutter
متدهای جستجوof(), findAncestor...()mount, update, unmount
عمومی بودنAPI عمومیpackage-internal
ارتباط با ویجتاز طریق فیلد widgetدارای widget و state

مثال‌های کد در Dart

استفاده اولیه از BuildContext برای دسترسی به تم و کوئری‌های رسانه:

dart
class ThemedText extends StatelessWidget {
  const ThemedText({super.key});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final media = MediaQuery.of(context);

    return Container(
      padding: EdgeInsets.all(media.size.width * 0.02),
      child: Text(
        'Styled Text',
        style: theme.textTheme.headlineMedium,
      ),
    );
  }
}

مثال با ناوبری از طریق BuildContext. Navigator.of(context) از کانتکست برای یافتن نزدیک‌ترین Navigator به بالا در درخت استفاده می‌کند:

dart
class _NavigateButtonState extends State<NavigateButton> {
  void _navigate() {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => const DetailsScreen(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _navigate,
      child: const Text('Go to Details'),
    );
  }
}

مثال یافتن اندازه ویجت از طریق BuildContext. متد findRenderObject() یک RenderObject برمی‌گرداند که از آن می‌توان اندازه را دریافت کرد:

dart
void _printSize(BuildContext context) {
  final renderBox = context.findRenderObject() as RenderBox?;
  if (renderBox != null) {
    print('Widget size: ${renderBox.size}');
  }
}

مهم: findRenderObject() اگر ویجت هنوز نصب نشده یا قبلاً حذف شده باشد، null برمی‌گرداند. همیشه نتیجه را قبل از استفاده از نظر null بررسی کنید. فراخوانی این متد در داخل build قبل از اتمام ساخت نیز ممکن است null برگرداند.

InheritedWidget و BuildContext

InheritedWidget — ویجت ویژه‌ای است که داده‌ها را به طور مؤثر از طریق BuildContext به پایین درخت منتشر می‌کند. وقتی ویجت فرزند MyInheritedWidget.of(context) را فرامی‌خواند، BuildContext در درخت بالا می‌رود، نزدیک‌ترین InheritedWidget از نوع مربوطه را پیدا می‌کند و داده‌های آن را برمی‌گرداند. در این فرآیند کانتکست در تغییرات مشترک می‌شود: اگر InheritedWidget تغییر کند، همه ویجت‌های مشترک به طور خودکار بازسازی می‌شوند.

ترکیب BuildContext + InheritedWidget جایگزین متغیرهای سراسری و prop-drilling (انتقال داده از طریق زنجیره سازنده‌ها) می‌شود. به جای انتقال تم از طریق 10 سطح ویجت، هر ویجت می‌تواند آن را مستقیماً از طریق Theme.of(context) دریافت کند. این کار کد را تمیزتر می‌کند و تعداد پارامترهای ارسالی را کاهش می‌دهد.

طبق گفته تیم Flutter (Google, آوریل 2026)، InheritedWidget آنقدر مکانیزم مؤثری است که تمام راه‌حل‌های رسمی مدیریت حالت بر اساس آن ساخته شده‌اند: Provider InheritedWidget را می‌پوشاند، Riverpod از آن به عنوان یکی از لایه‌ها استفاده می‌کند و خود Flutter SDK (Theme, MediaQuery, Navigator, Localizations) کاملاً بر این معماری مبتنی است.

ایجاد InheritedWidget خود

ایجاد InheritedWidget خودتان امکان انتشار داده‌ها را بدون وابستگی‌های خارجی فراهم می‌کند. کلاس InheritedWidget را گسترش می‌دهد و متد استاتیک of(BuildContext context) را ارائه می‌کند. این یک جایگزین مینیمالیستی برای Provider در سناریوهای ساده است:

dart
class AppConfig extends InheritedWidget {
  final String apiUrl;
  final bool useDarkMode;

  const AppConfig({
    super.key,
    required this.apiUrl,
    required this.useDarkMode,
    required super.child,
  });

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

  @override
  bool updateShouldNotify(AppConfig oldWidget) {
    return apiUrl != oldWidget.apiUrl || useDarkMode != oldWidget.useDarkMode;
  }
}

حالا هر ویجتی پایین‌تر در درخت می‌تواند به پیکربندی دسترسی داشته باشد: final config = AppConfig.of(context);. اگر پیکربندی تغییر کند، همه ویجت‌های مشترک به طور خودکار بازسازی خواهند شد.

خطاهای رایج

اولین خطای رایج — ذخیره BuildContext پس از dispose یا استفاده از آن در callback ناهمگام بدون بررسی mounted. BuildContext به عنصر متصل است و عنصر می‌تواند نابود شود (زمانی که ویجت از درخت حذف می‌شود). استفاده از کانتکست پس از نابودی عنصر منجر به استثنا می‌شود. راه‌حل — استفاده از context.mounted (در نسخه‌های جدید Flutter موجود است) یا بررسی mounted در State.

دومین خطا — فراخوانی Theme.of(context) در initState. در مرحله initState کانتکست هنوز به طور کامل در درخت نصب نشده است. جستجوی InheritedWidget در initState ممکن است null برگرداند یا استثنا ایجاد کند. تمام فراخوانی‌های of(context) باید در build یا didChangeDependencies انجام شوند، جایی که کانتکست تضمیناً در درخت قرار دارد.

سومین خطا — استفاده از BuildContext یک ویجت برای دستکاری ویجت دیگر. BuildContext برای تعامل بین ویجتی خارج از سلسله‌مراتب «والد-فرزند» طراحی نشده است. اگر نیاز به مدیریت حالت ویجت دیگری دارید — از callbackها، کنترلرها یا ابزارهای مدیریت حالت استفاده کنید.

چهارمین خطا — ارسال BuildContext به یک تابع ناهمگام که از dispose ویجت عبور می‌کند. سناریوی معمول: Navigator.of(context) در یک متغیر ذخیره شده و بعد از اینکه کاربر صفحه را ترک کرده استفاده می‌شود. راه‌حل — کانتکست را در اشیاء استاتیک یا طولانی‌عمر ذخیره نکنید.

کانتکست در عملیات‌های ناهمگام

الگوی ایمنی برای کار با BuildContext در عملیات‌های ناهمگام: همیشه قبل از استفاده از کانتکست mounted را بررسی کنید و کانتکست را در بسته‌هایی که ممکن است از ویجت بیشتر عمر کنند ذخیره نکنید:

dart
Future<void> _safeNavigation(BuildContext context) async {
  await Future.delayed(const Duration(seconds: 2));
  if (!context.mounted) return;
  Navigator.of(context).push(MaterialPageRoute(...));
}

بهترین روش‌ها

کار با BuildContext نیازمند درک چرخه حیات و محدودیت‌های آن است. قانون اول: فقط در داخل متدهایی که کانتکست را به عنوان پارامتر دریافت می‌کنند از آن استفاده کنید (build, didChangeDependencies). کانتکست را در فیلدهای کلاس یا متغیرهای استاتیک ذخیره نکنید — این تقریباً همیشه منجر به باگ می‌شود.

قانون دوم: برای دسترسی به داده‌های InheritedWidget به جای build از didChangeDependencies استفاده کنید. اگر داده‌ها فقط برای مقداردهی اولیه لازم هستند، نه برای رندر، didChangeDependencies مکان مناسبی است. این کار اجازه می‌دهد منطق مقداردهی را از ساخت UI جدا کنید و از فراخوانی‌های تکراری در هر به‌روزرسانی جلوگیری کنید.

قانون سوم: هنگام کار با عملیات‌های ناهمگام از callbackهایی استفاده کنید که به کانتکست وابسته نیستند یا mounted را بررسی کنید. اگر عملیات ناهمگام نیاز به ناوبری یا دسترسی به تم دارد، این داده‌ها را زودتر (در کانتکست همگام build یا initState) دریافت کرده و در متغیرهای محلی ذخیره کنید، نه در کانتکست.

چه زمانی کانتکست لازم است و چه زمانی نه

  • لازم است: دسترسی به Theme, MediaQuery, Navigator, Localizations, ScaffoldMessenger
  • لازم است: جستجوی RenderObject برای اندازه‌گیری ابعاد
  • لازم است: ایجاد SnackBar, BottomSheet, Dialog
  • لازم نیست: فراخوانی متدهای منطق کسب‌وکار، درخواست‌های HTTP، کار با پایگاه داده
  • لازم نیست: ساخت ویجت خارج از build (در کارخانه‌ها، سازنده‌ها)

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

BuildContext در Flutter چیست؟

BuildContext — رابطی است که موقعیت ویجت را در درخت عناصر نشان می‌دهد. از طریق آن ویجت به محیط دسترسی پیدا می‌کند: تم، کوئری‌های رسانه، ناوبر و داده‌های InheritedWidget. هر ویجت کانتکست منحصربه‌فرد خود را دارد.

BuildContext چگونه کار می‌کند؟

BuildContext درخت را از عنصر جاری به سمت ریشه پیمایش می‌کند و نزدیک‌ترین InheritedWidget از نوع درخواستی را پیدا می‌کند. متد dependOnInheritedWidgetOfExactType نه تنها داده‌ها را پیدا می‌کند، بلکه ویجت را در تغییرات آنها مشترک می‌کند — با به‌روزرسانی InheritedWidget ویجت به طور خودکار بازسازی می‌شود.

چرا نمی‌توان BuildContext را در فیلدهای کلاس ذخیره کرد؟

BuildContext به عنصر در درخت متصل است و عنصر می‌تواند نابود شود (ویجت حذف شود). استفاده از کانتکست ذخیره‌شده پس از حذف ویجت منجر به استثنا می‌شود. اگر کانتکست در callback ناهمگام نیاز است — قبل از استفاده mounted را بررسی کنید.

تفاوت بین BuildContext و Element چیست؟

BuildContext یک رابط است و Element — پیاده‌سازی. توسعه‌دهنده از طریق BuildContext کار می‌کند بدون اینکه نوع خاص عنصر را بداند. Element — مکانیزم داخلی Flutter است که Widget را به RenderObject متصل می‌کند و چرخه حیات را مدیریت می‌کند.

آیا می‌توان BuildContext ویجت دیگری را دریافت کرد؟

دسترسی مستقیم به کانتکست ویجت دیگر وجود ندارد. برای کانتکست والد از context.findAncestorStateOfType برای State یا کلیدها (GlobalKey) استفاده کنید. برای فرزند — callback ارسال کنید. BuildContext برای دسترسی بین ویجتی خارج از سلسله‌مراتب طراحی نشده است.

خلاصه

  • BuildContext — شیء بنیادی Flutter که موقعیت ویجت را در درخت نشان می‌دهد و از طریق InheritedWidget دسترسی به محیط سلسله‌مراتبی را فراهم می‌کند
  • مکانیزم جستجو — BuildContext درخت را از پایین به بالا پیمایش می‌کند، نزدیک‌ترین InheritedWidget از نوع درخواستی را پیدا می‌کند و در تغییرات آن مشترک می‌شود
  • استفاده اصلی — Theme.of(context), MediaQuery.of(context), Navigator.of(context) برای دسترسی به تم‌ها، تطبیق‌پذیری و ناوبری
  • BuildContext در مقابل Element — BuildContext رابط عمومی است، Element — پیاده‌سازی خصوصی. توسعه‌دهنده همیشه از طریق BuildContext کار می‌کند
  • چرخه حیات — BuildContext تا زمانی که عنصر مربوطه زنده است زنده می‌ماند; پس از dispose کانتکست نباید استفاده شود
  • خطاها — ذخیره کانتکست در اشیاء طولانی‌عمر، استفاده در initState، استفاده پس از dispose — منابع رایج باگ
  • قانون — فقط در build/didChangeDependencies از BuildContext استفاده کنید، آن را ذخیره نکنید، در سناریوهای ناهمگام mounted را بررسی کنید

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

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

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

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