BuildContext — شیء بنیادی Flutter است که موقعیت یک ویجت خاص را در درخت عناصر نشان میدهد و دسترسی به محیط آن را فراهم میکند. طبق مستندات رسمی Flutter (Flutter.dev, 2026)، BuildContext پلی بین ویجت و فریمورک است: از طریق آن ویجت تم (Theme)، کوئریهای رسانه (MediaQuery)، بومیسازی (Localizations) و دادههای InheritedWidget را دریافت میکند. هر ویجت BuildContext مخصوص خود را دارد که به عنوان اولین آرگومان به متد build ارسال میشود.
نکات اصلی
BuildContext — رابطی است که توسط کلاس Element پیادهسازی میشود و به ویجت اطلاعاتی درباره مکان آن در سلسلهمراتب UI میدهد. هر نمونه BuildContext برای موقعیت خاصی در درخت منحصربهفرد است و نمیتواند به جای دیگری منتقل شود. اگر ویجت والد خود را تغییر دهد (مثلاً به ظرف دیگری منتقل شود)، BuildContext جدیدی دریافت میکند.
هدف اصلی BuildContext دسترسی به InheritedWidget است. از طریق کانتکست، ویجت نزدیکترین نمونه Theme، MediaQuery، Navigator یا Directionality را با بالا رفتن در درخت پیدا میکند. این مکانیزم اساس کل سیستم تم، ناوبری و چیدمان تطبیقی در Flutter است. بدون BuildContext هیچ ویجتی نمیتواند این دادهها را دریافت کند.
طبق مستندات معماری Flutter (Google, 2026)، BuildContext همچنین برای یافتن شیء RenderObject مرتبط با ویجت، اندازهگیری ابعاد و موقعیتیابی استفاده میشود. متدهایی مانند findRenderObject() و size دقیقاً از طریق کانتکست قابل دسترسی هستند. کانتکست همچنین از طریق Localizations.of(context) دسترسی به بومیسازی را فراهم میکند.
درک معماری مهم: BuildContext رابطی است که Element آن را پیادهسازی میکند، نه Widget. Element «چسب» بین Widget (پیکربندی) و RenderObject (نمایش واقعی) است. وقتی در مستندات «کانتکست ویجت» گفته میشود، منظور عنصری است که آن ویجت را مدیریت میکند. متد build دقیقاً چنین کانتکستی دریافت میکند — کانتکست ویجت در حال ایجاد، نه ویجتهای فرزند برگشتی.
مکانیزم کار 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 — پیادهسازی آن. در کد Flutter شما همیشه از طریق رابط BuildContext کار میکنید، بدون اینکه نوع خاص عنصر (StatelessElement, StatefulElement, ProxyElement و غیره) را بدانید. این عمدی است: توسعهدهنده نیازی به دانستن جزئیات پیادهسازی عنصر ندارد — رابط برای دسترسی به محیط کافی است.
انواع مختلف عناصر BuildContext را متفاوت پیادهسازی میکنند: StatelessElement simplemente فراخوانیهای build را منتقل میکند، StatefulElement State را مدیریت میکند و InheritedElement اشتراکها را از طریق dependOnInheritedWidgetOfExactType ردیابی میکند. اما از دید توسعهدهنده همه آنها BuildContext با API یکسان هستند.
| جنبه | BuildContext | Element |
|---|---|---|
| نوع | رابط (abstract class) | کلاس پیادهسازی |
| استفاده | توسط توسعهدهنده در build | مکانیزم داخلی Flutter |
| متدهای جستجو | of(), findAncestor...() | mount, update, unmount |
| عمومی بودن | API عمومی | package-internal |
| ارتباط با ویجت | از طریق فیلد widget | دارای widget و state |
استفاده اولیه از BuildContext برای دسترسی به تم و کوئریهای رسانه:
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 به بالا در درخت استفاده میکند:
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 برمیگرداند که از آن میتوان اندازه را دریافت کرد:
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 به پایین درخت منتشر میکند. وقتی ویجت فرزند 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 را گسترش میدهد و متد استاتیک of(BuildContext context) را ارائه میکند. این یک جایگزین مینیمالیستی برای Provider در سناریوهای ساده است:
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 را بررسی کنید و کانتکست را در بستههایی که ممکن است از ویجت بیشتر عمر کنند ذخیره نکنید:
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) دریافت کرده و در متغیرهای محلی ذخیره کنید، نه در کانتکست.
سوالات متداول
BuildContext — رابطی است که موقعیت ویجت را در درخت عناصر نشان میدهد. از طریق آن ویجت به محیط دسترسی پیدا میکند: تم، کوئریهای رسانه، ناوبر و دادههای InheritedWidget. هر ویجت کانتکست منحصربهفرد خود را دارد.
BuildContext درخت را از عنصر جاری به سمت ریشه پیمایش میکند و نزدیکترین InheritedWidget از نوع درخواستی را پیدا میکند. متد dependOnInheritedWidgetOfExactType نه تنها دادهها را پیدا میکند، بلکه ویجت را در تغییرات آنها مشترک میکند — با بهروزرسانی InheritedWidget ویجت به طور خودکار بازسازی میشود.
BuildContext به عنصر در درخت متصل است و عنصر میتواند نابود شود (ویجت حذف شود). استفاده از کانتکست ذخیرهشده پس از حذف ویجت منجر به استثنا میشود. اگر کانتکست در callback ناهمگام نیاز است — قبل از استفاده mounted را بررسی کنید.
BuildContext یک رابط است و Element — پیادهسازی. توسعهدهنده از طریق BuildContext کار میکند بدون اینکه نوع خاص عنصر را بداند. Element — مکانیزم داخلی Flutter است که Widget را به RenderObject متصل میکند و چرخه حیات را مدیریت میکند.
دسترسی مستقیم به کانتکست ویجت دیگر وجود ندارد. برای کانتکست والد از context.findAncestorStateOfType برای State یا کلیدها (GlobalKey) استفاده کنید. برای فرزند — callback ارسال کنید. BuildContext برای دسترسی بین ویجتی خارج از سلسلهمراتب طراحی نشده است.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید