چگونه در Flutter خارج از کد UI همیشه یک BuildContext داشته باشیم
BuildContext اطلاعات مهم پیکربندی گسترده برنامه را برای همه ویجت های درخت ویجت فراهم می کند. همیشه به طور طبیعی در متدهای ساخت و در کلاس های State موجود است.
در این مقاله، تحلیل خواهیم کرد که چگونه می توانیم یک BuildContext معتبر خارج از محدوده در دسترس بودن طبیعی آن به دست آوریم.
فهرست مطالب
نیاز به BuildContext خارج از کد UI
داشتن NavigatorKey در سطح جهانی
یادداشتی در مورد Navigator 2.0
استفاده از NavigatorKey's NavigationState برای نشان دادن نان تست
معرفی
Flutter یک جعبه ابزار UI برای ساخت برنامه های دسکتاپ، موبایل و وب است. Flutter چارچوبی است که به شما امکان می دهد برنامه هایی را به زبان برنامه نویسی دارت بسازید.
در Flutter، با نوشتن ویجت ها، رابط کاربری ایجاد می کنید. ویجت هر قطعه منطقی از آنچه روی صفحه نمایش قابل رندر است مانند نماد، تصویر، متن و غیره است.
نوشتن ویجت ها شامل تنظیم آنها به عنوان یک کودک یا فرزند سایر ویجت های والدین است. به عبارت دیگر، شما در حال ساختن یک درخت ویجت هستید. همه ویجتهای درخت رابط کاربری شما به BuildContext دسترسی دارند.
ماهیت BuildContext در فلاتر
BuildContext اطلاعات مهم پیکربندی گسترده برنامه را برای همه ویجت های درخت ویجت فراهم می کند.
BuildContext عبارت است از فلوتر کردن ویجتها که آب برای بدن ما چیست. همانطور که آب با مواد مغذی و اکسیژن در اطراف سیستم ما گردش می کند، BuildContext نیز مقادیر خاص زمان اجرا را برای هر ویجت در برنامه های ما ارائه می دهد.
در Flutter، معمولاً با گسترش کلاس های StatelessWidget یا StatefulWidget (و وضعیت آن) یک ویجت ایجاد می کنید.
در هر دو مورد، شما باید روش ساخت را نادیده بگیرید. در روش ساخت باید ویجت دیگری را برگردانید. به این ترتیب، شما به ساختن درخت ویجت ادامه می دهید.
همه متدهای ساخت تنها یک آرگومان دارند: BuildContext. برای رندر کردن رابط کاربری به BuildContext در Flutter نیاز دارید. ضروری است. چارچوب Flutter خود BuildContext را برای روش ساخت هر ویجت فراهم می کند.
BuildContext به شما امکان دسترسی به گیرنده ها و روش هایی مانند:
findAncestorStateOfType
: یک متد تایپ شده که شی State
StatefulWidget
مشخص شده را برمی گرداند.
getInheritedWidgetOfExactType
: یک روش تایپ شده که یک نوع ویجت درخواستی را که در بالای درخت ویجت یافت می شود، برمی گرداند.
mounted
: bool
که می گوید آیا ویجت در زمینه در نظر است یا خیر.
دریافت کننده ها و روش های مفید دیگری در BuildContext وجود دارد. خارج از آنها، BuildContext نیز مهم است، زیرا از طریق آن، میتوانیم نمونه فعال برنامه از کلاسهای خاص را بدست آوریم. به عنوان مثال: MediaQuery، Navigator، Theme و غیره.
یک الگوی رایج این است که متد .of
را در کلاس مورد نظر فراخوانی کنیم و BuildContext (یا فقط "context") خود را برای این متد .of
ارائه دهیم.
final isLandscape = MediaQuery.of(context).orientation == Orientation.landscape; final hasConfirmed = Navigator.of(context).pop(false); final lightTheme = Theme.of(context).copyWith(brightness: Brightness.light);
.of
رایج ترین الگو است. با این حال، موارد استفاده مستقیمتر، خاصتر و نادرتری از چنین گیرندههای استاتیکی وجود دارد که به زمینه متکی هستند.
final isDarkTheme = Theme.brightnessOf(context) == Brightness.dark;
شما همچنین می توانید دریافت کننده های ایستا از نمونه های کلاس های خود را ایجاد کنید که به BuildContext متکی هستند. میتوانید درباره نحوه انجام این کار (از طریق InheritedWidget) در اینجا اطلاعات بیشتری کسب کنید .
همانطور که می بینیم، دلایل زیر دلیل نیاز به BuildContext است:
برای ساخت ویجت ها،
برای دسترسی به منابع،
برای به دست آوردن نمونه هایی از کلاس ها،
و غیره
نیاز به BuildContext خارج از کد UI
BuildContext به طور طبیعی در تمام متدهای ساخت و در کلاس State
StatefulWidgets
موجود است. اکثر فراخوانیهای متد با زمینه در این محدودهها همانطور که در نظر گرفته شده است کار خواهند کرد.
در موارد جزئی، شما باید از جایی که به طور طبیعی در دسترس نیست به BuildContext دسترسی داشته باشید. شما می خواهید نمونه های زمان اجرا را خارج از کد UI (خارج از روش های ساخت و کلاس های State
) بدست آورید.
این معمولاً دشوار است زیرا ما نمیتوانیم یک BuildContext خودمان را نمونهسازی کنیم. ما فقط زمانی به یکی دسترسی داریم که برنامه ما شروع به اجرا کرد.
دلایل رایج دسترسی به BuildContext از این طریق، یا هنگام انجام ناوبری، هنگام نمایش هشدارها و مدال ها در کدهای غیر UI، یا هنگام راه اندازی به روز رسانی UI از تغییرات در معماری حالت برنامه است.
همچنین، هنگامی که یک کاربر یک اعلان را باز می کند، قصد داریم کاربر را به صفحه هدف هدایت کنیم اگر برنامه از قبل باز است. برای انجام این کار به BuildContext نیاز داریم.
داشتن یک NavigatorKey در سطح جهانی
یک راه حل برای همیشه داشتن یک BuildContext در Flutter این است که خودتان در ابتدای برنامه یک BuildContext ارائه دهید.
برترین ویجت برنامه های Flutter معمولاً CupertinoApp
، MaterialApp
یا WidgetsApp
است. به عبارت دیگر، آنها نقطه ورود برنامه ما هستند.
این ویجت های "برنامه" یک پارامتر اختیاری navigatorKey
را می گیرند. دارای نوع GlobalKey<NavigatorState>
است. این GlobalKey ویژه NavigatorState دارای یک BuildContext پس از اجرای برنامه است.
وقتی یک NavigatorKey ایجاد میکنید و آن را به بالاترین ابزارک «برنامه» میدهید، برنامه Flutter شما BuildContext را که استفاده میکند در navigatorKey شما نگه میدارد. به این ترتیب، هر جا که به navigatorKey دسترسی دارید، می توانید به BuildContext دسترسی داشته باشید.
NavigatorKey را به عنوان یک متغیر نهایی ثابت از یک کلاس ابزار جهانی ایجاد و در معرض دید قرار دهید. آن را به بالاترین "برنامه" ویجت ارائه دهید. سپس در هر کجا که به آن نیاز دارید، زمینه را از این کلاس جهانی در دسترس دریافت کنید.
/* In services/navigation_service.dart */ class NavigationService { static final navigatorKey = GlobalKey<NavigatorState>(); // ... other methods and getters } /* In main.dart */ import 'package:flutter/material.dart'; import 'services/navigation_service.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( navigatorKey: NavigationService.navigatorKey, // line of interest home: const Scaffold(body: Center(child: Text('BuildContext'))), ); } } /* In services/modal_service.dart */ import 'package:flutter/material.dart'; import 'services/navigation_service.dart'; Future<T?> showModal<T>(Widget modal) async { return await showModalBottomSheet<T>( context: NavigationService.navigatorKey.currentContext!, backgroundColor: Colors.transparent, isScrollControlled: true, builder: (_) => modal, ); }
در مثال بالا، می توانید ببینید که چگونه می توانیم showModalBottomSheet
از یک فایل غیر ویجت با استفاده از زمینه از navigatorKey فراخوانی کنیم. این تنها در صورتی کار می کند که این کلید همانطور که در قطعه کد نشان داده شده است به بالاترین ویجت "برنامه" متصل شده باشد.
این روش دسترسی به BuildContext همیشه وجود داشته است. ما می توانیم آن را در حال استفاده در Stacked Architecture ببینیم .
میتوانید NavigatorKey را در کلاسی کاملاً متفاوت از برخی از NavigationService نگه دارید. همچنین می تواند یک متغیر جهانی مستقل در پروژه فلاتر شما باشد. علاوه بر این، می توانید آن را از طریق معماری مدیریت دولتی که استفاده می کنید در دسترس قرار دهید.
نحوه در دسترس قرار دادن NavigatorKey در سطح جهانی به شما بستگی دارد. چیزی که لازم است این است که در مواقعی که به BuildContext خارج از کد UI نیاز دارید، یکی را در اختیار Flutter قرار دهید.
یادداشتی در مورد Navigator 2.0
یک پیکربندی خاص وجود دارد که در این مرحله قابل ذکر است.
برترین ابزارکهای برنامه (CupertinoApp، MaterialApp و WidgetsApp) همگی یک سازنده .router
دارند. این سازنده با معادل مستقیم متفاوت است زیرا برنامه ای ایجاد می کند که به جای Navigator
از یک Router
استفاده می کند.
به طور خاص، برنامههای .router
به ما اجازه میدهند تا به جای پیمایش ضروری ( navigator.push
و navigator.pop
) پیمایش اعلامی ( router.go
) را انجام دهیم.
پیمایش اعلامی با پیوندهای عمیق (زمانی که برنامه های تلفن همراه URL ها را باز می کنند) و با سابقه مرورگر (یک امتیاز مثبت برای وب Flutter) به خوبی کار می کند.
این سازنده های .router
آرگومان navigatorKey را نمی گیرند. با این حال، آنها می توانند کلاس های مختلف روتر خاص را به عنوان آرگومان بپذیرند. در این کلاس ها می توانید به هر طریقی یک navigatorKey ارائه دهید.
به عنوان مثال، go_router
یک بسته شناخته شده برای Navigator 2.0 در Flutter است. پیچیدگیهای آن کلاسهای خاص روتر را خلاصه میکند.
اکنون، سازنده GoRouter
، همانطور که انتظار دارید، یک آرگومان navigatorKey را می پذیرد. به این ترتیب، اگر از آن استفاده می کنید، همچنان می توانید به BuildContext در هر کد غیر UI در پروژه Flutter خود دسترسی داشته باشید.
استفاده از NavigatorKey's NavigationState برای نشان دادن نان تست
مزیت اضافه شده داشتن یک NavigatorKey پیوست زمانی است که قصد داریم اطلاعات را برای کاربر تست کنیم.
ما میتوانیم این کار را با گفت ن یک OverlayEntry
(با ابزارک نان تست) به overlay
NavigatorState
که به navigatorKey ما متصل است، انجام دهیم.
با کمک یک Timer
، نان تست پس از چند ثانیه حذف می شود:
/* In services/toast_service.dart */ import 'dart:async'; import 'navigation_service.dart'; Widget _toast(String text) => Container( padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: Colors.teal, ), child: Text(text), ); class ToastService { static OverlayEntry? _entry; static Timer? _timer; static show(String text) { _dismiss(); _entry = OverlayEntry(builder: (context) => _Toast(text)); NavigationService.navigatorKey.currentState!.overlay!.insert(_entry!); _timer = Timer(const Duration(seconds: 5), _dismiss); } static _dismiss() { try { _timer?.cancel(); _timer = null; _entry?.remove(); _entry = null; } catch () { } } } /* In services/auth_service.dart */ import 'toast_service.dart'; class AuthService { static void login({required String email, required String password}) async { // ... calling the API // toast after a successful login ToastService.show('Welcome Back!'); } }
خلاصه
"context" چیزی است که همیشه در Flutter از آن استفاده خواهید کرد.
وقتی خارج از روش ساخت (یا کلاسهای State) به آن نیاز دارید، یک navigatorKey ایجاد کنید و آن را به بالاترین ویجت «برنامه» وصل کنید.
سپس میتوانید به همان زمینهای که کد رابط کاربری شما از این کلید استفاده میکند، از هر نقطهای در پروژه Flutter دسترسی داشته باشید.
به سلامتی!
ارسال نظر