التنقل بين الشاشات في Flutter: Navigator
نادراً ما يكون التطبيق شاشة واحدة. تضغط على منتج فتنتقل لصفحة تفاصيله، وتضغط «رجوع» فتعود للقائمة. وأحياناً تختار شيئاً في شاشة فيظهر اختيارك في الشاشة السابقة. في هذا الدرس ستتعلم كل هذا باستخدام Navigator، المسؤول عن التنقل في Flutter.
فكرة الكومة (Stack)
Section titled “فكرة الكومة (Stack)”Navigator يرتب الشاشات مثل كومة أطباق: كل شاشة جديدة تُوضع فوق السابقة، والرجوع يرفع الطبق العلوي فتظهر الشاشة التي تحته.
push: ضع شاشة جديدة فوق الكومة.pop: ارفع الشاشة العلوية وارجع للسابقة.
فتح شاشة جديدة
Section titled “فتح شاشة جديدة”ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => const DetailsScreen(courseName: 'Flutter'), ), ); }, child: const Text('افتح تفاصيل الكورس'),)Navigator.push: افتح شاشة جديدة.context: موقعنا الحالي في شجرة الـ Widgets، ويحتاجه Navigator ليعرف من أين ننتقل.MaterialPageRoute: طريقة الانتقال بحركة Material المعتادة، وbuilderدالة تُرجع الشاشة الجديدة.
تمرير البيانات للشاشة الجديدة
Section titled “تمرير البيانات للشاشة الجديدة”لاحظ DetailsScreen(courseName: 'Flutter'): البيانات تُمرر عبر المُنشئ كأي Widget، تماماً كما تعلمت في درس Stateless:
class DetailsScreen extends StatelessWidget { final String courseName;
const DetailsScreen({super.key, required this.courseName});
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('كورس $courseName')), body: Center( child: ElevatedButton( onPressed: () { Navigator.pop(context); }, child: const Text('رجوع'), ), ), ); }}عند الضغط، تظهر شاشة جديدة عنوانها «كورس Flutter». و AppBar يضيف زر رجوع تلقائياً في الشريط العلوي، لأنه يعرف أن هناك شاشة تحته. وزرنا «رجوع» يفعل نفس الشيء بـ Navigator.pop. وعلى أندرويد يعمل زر الرجوع في الهاتف نفسه أيضاً.
استقبال نتيجة من الشاشة
Section titled “استقبال نتيجة من الشاشة”أحياناً تفتح شاشة لتختار منها شيئاً، مثل اختيار لون أو صورة، ثم تريد الاختيار في الشاشة الأولى. pop يمكنه إرجاع قيمة، و push يُرجع Future تنتظره بـ await:
class _HomeScreenState extends State<HomeScreen> { String _chosenColor = 'لم تختر بعد';
Future<void> _pickColor() async { final result = await Navigator.push<String>( context, MaterialPageRoute(builder: (context) => const ColorScreen()), ); if (result != null) { setState(() { _chosenColor = result; }); } }
// ... وفي build: // ElevatedButton(onPressed: _pickColor, child: const Text('اختر لونك المفضل')), // Text('لونك: $_chosenColor'),}وشاشة الاختيار ترجع اللون مع pop:
class ColorScreen extends StatelessWidget { const ColorScreen({super.key});
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('اختر لوناً')), body: Column( children: [ for (final color in ['أحمر', 'أخضر', 'أزرق']) ListTile( title: Text(color), onTap: () { Navigator.pop(context, color); }, ), ], ), ); }}ماذا يحدث:
- في البداية تعرض الشاشة الرئيسية «لونك: لم تختر بعد».
- الضغط على «اختر لونك المفضل» يفتح شاشة الألوان، و
awaitتنتظر. - عند الضغط على «أزرق»، يرجع
Navigator.pop(context, color)القيمة'أزرق'ويغلق الشاشة. - تكمل
_pickColorعملها، فتصبح الشاشة: «لونك: أزرق».
هذا تطبيق مباشر لما تعلمته في درس async و await بكورس Dart! والفحص if (result != null) ضروري، لأن المستخدم قد يرجع بزر الرجوع دون أن يختار شيئاً، فتكون النتيجة null.
لاحظ أيضاً for داخل children: حلقة داخل القائمة تولّد عنصراً لكل لون، وهي «collection for» أخت «collection if».
المسارات المسماة
Section titled “المسارات المسماة”في التطبيقات الكبيرة يمكنك إعطاء كل شاشة اسماً في MaterialApp:
MaterialApp( initialRoute: '/', routes: { '/': (context) => const HomeScreen(), '/details': (context) => const DetailsScreen(courseName: 'Dart'), },)ثم تنتقل بالاسم من أي مكان: Navigator.pushNamed(context, '/details'). هذا يشبه المسارات المسماة في مواقع الويب.
pushReplacement: بلا رجوع
Section titled “pushReplacement: بلا رجوع”بعد تسجيل الدخول بنجاح، لا تريد أن يضغط المستخدم «رجوع» فيعود لشاشة الدخول! استخدم Navigator.pushReplacement بدلاً من push: تفتح الشاشة الجديدة وتحذف الحالية من الكومة.
جرّب بنفسك
Section titled “جرّب بنفسك”- أنشئ شاشتين: رئيسية وتفاصيل، وانتقل بينهما بـ
pushوpop. - مرّر اسم الكورس للشاشة الثانية واعرضه في عنوانها.
- ابنِ شاشة اختيار الألوان، واعرض اللون المختار في الشاشة الرئيسية.
- تحدٍّ: اجعل خلفية الشاشة الرئيسية تتغير فعلاً للون المختار (أرجع
Colorبدلاً من نص).
أخطاء شائعة
Section titled “أخطاء شائعة”- pop في الشاشة الأولى: لا توجد شاشة تحتها، فقد يظهر شاشة سوداء أو يُغلق التطبيق.
- نسيان فحص null عند استقبال النتيجة: المستخدم قد يرجع دون اختيار.
- تمرير البيانات بمتغيرات عامة: مرّرها عبر المُنشئ دائماً، فهذا أوضح وأأمن.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
أي أمر يغلق الشاشة الحالية ويعود للسابقة مع إرجاع قيمة؟
pop يرفع الشاشة العلوية من الكومة، والمعامل الثاني هو القيمة التي تصل لمن انتظر push.
سؤال
بعد تسجيل الدخول، أي أمر يمنع المستخدم من الرجوع لشاشة الدخول؟
pushReplacement تستبدل الشاشة الحالية بالجديدة، فلا يبقى شيء تحتها للرجوع إليه.
الخلاصة
Section titled “الخلاصة”Navigatorيرتب الشاشات ككومة:pushتضيف فوقها، وpopترفع العلوية.MaterialPageRouteمعbuilderيحدد الشاشة الجديدة، والبيانات تُمرر عبر المُنشئ.pop(context, value)يرجع نتيجة، وawait Navigator.pushيستقبلها، مع فحص null.- المسارات المسماة في
routesوpushNamed، و go_router للتطبيقات الكبيرة. pushReplacementللانتقال بلا رجوع، مثل ما بعد تسجيل الدخول.