أساسيات Navigator 1.0
أساسيات Navigator 1.0
يعتمد Navigator 1.0 في Flutter على نظام تنقل أمري (imperative) قائم على المكدس (stack) ومدمج في الإطار. تخيّله كمكدّس فعلي من الشاشات: كل صفحة جديدة تفتحها تُضاف فوق المكدس، وعند الضغط على زر الرجوع تُزال الصفحة العليا لتكشف الشاشة السابقة. فهم هذا النموذج الذهني هو أساس كل التنقل في Flutter.
تُدير ودجت Navigator مكدساً من كائنات Route. يحتوي كل Route على واجهة مستخدم كاملة الشاشة (أو نافذة مشروطة)، وحركة الانتقال، ودورة حياته. أكثر أنواع المسارات شيوعاً هو MaterialPageRoute، الذي يوفر تلقائياً انتقال الانزلاق الصحيح للمنصة على Android، وانزلاقاً من اليمين بأسلوب Cupertino على iOS.
الوصول إلى Navigator
تصل إلى أقرب Navigator في شجرة الودجات عبر BuildContext. يوفر Flutter عدة توابع ثابتة مريحة:
Navigator.push(context, route)— يدفع مساراً جديداً إلى المكدس.Navigator.pop(context)— يزيل المسار العلوي من المكدس.Navigator.pushNamed(context, routeName)— يدفع مساراً مسمى (يتطلب جدول مسارات فيMaterialApp).Navigator.pushReplacement(context, route)— يستبدل المسار الحالي دون إبقائه في المكدس (مفيد لانتقالات تسجيل الدخول → الرئيسية).Navigator.popUntil(context, predicate)— يزيل المسارات حتى يُرجع الشرطtrue.
دفع شاشة جديدة باستخدام MaterialPageRoute
MaterialPageRoute هو فئة فرعية من PageRoute تبني ودجتك داخل انتقال صفحة Material قياسي. معامله الإلزامي هو رد نداء builder يستقبل BuildContext ويُرجع ودجت الوجهة.
الانتقال إلى شاشة التفاصيل
// الشاشة الأولى
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('الرئيسية')),
body: Center(
child: ElevatedButton(
onPressed: () {
// دفع DetailScreen إلى مكدس التنقل
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailScreen(itemId: 42),
),
);
},
child: const Text('اذهب إلى التفاصيل'),
),
),
);
}
}
// الشاشة الثانية
class DetailScreen extends StatelessWidget {
final int itemId;
const DetailScreen({super.key, required this.itemId});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('عنصر $itemId')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('رجوع'),
),
),
);
}
}
AppBar تلقائياً زر الرجوع عندما يوجد مسار أسفلها في المكدس — لا تحتاج إلى إضافة Navigator.pop لسهم الرجوع. استدعاء pop اليدوي ضروري فقط لأزرار الرجوع المخصصة أو التنقل البرمجي.إرجاع البيانات من شاشة
من أقوى مزايا Navigator 1.0 إمكانية إرجاع البيانات من شاشة مدفوعة. تُرجع Navigator.push كائن Future يكتمل عند إزالة المسار المدفوع. تُمرر النتيجة إلى Navigator.pop(context, result).
تمرير البيانات إلى الشاشة المستدعية
// المستدعي: انتظر النتيجة من شاشة الاختيار
Future<void> _openColorPicker(BuildContext context) async {
final String? selectedColor = await Navigator.push<String>(
context,
MaterialPageRoute(
builder: (context) => const ColorPickerScreen(),
),
);
if (selectedColor != null) {
// استخدم القيمة المُرجعة
print('اختار المستخدم: $selectedColor');
}
}
// تُزيل ColorPickerScreen نفسها مع القيمة المختارة
class ColorPickerScreen extends StatelessWidget {
const ColorPickerScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('اختر لوناً')),
body: ListView(
children: ['أحمر', 'أخضر', 'أزرق'].map((color) {
return ListTile(
title: Text(color),
onTap: () => Navigator.pop(context, color), // إرجاع القيمة
);
}).toList(),
),
);
}
}
دورة حياة مكدس التنقل
فهم كيفية تأثير المسارات على دورات حياة الودجات يمنع أخطاء الذاكرة والحالة الشائعة:
- عند دفع مسار، تُبنى شجرة الودجات الخاصة به ويُستدعى
initStateعلى أيStatefulWidgetفيها. - المسار السابق يبقى حياً في الذاكرة لكنه محجوب؛ لا تُعاد بناء ودجاته أثناء الإخفاء.
- عند إزالة مسار، يُستدعى
disposeعلى جميعStatefulWidgets في ذلك المسار — يجب تنظيف وحدات التحكم والتدفقات والمستمعين هناك. - إذا أردت تشغيل كود في كل مرة تصبح فيها الشاشة مرئية (مثل العودة من مسار فرعي)، استخدم
RouteObserverأو استدع دالة تحديث فيFutureالذي تُرجعهNavigator.push.
Navigator.pop أبداً إذا كان المسار الحالي هو آخر مسار في المكدس. سيُغلق التطبيق بالكامل على Android أو يُلقي خطأ. استخدم Navigator.canPop(context) للتحقق عند الشك.المسارات المسمّاة (نظرة عامة سريعة)
في التطبيقات التي تحتوي على شاشات كثيرة، يُبقي تسجيل أسماء المسارات في MaterialApp.routes استدعاءات التنقل نظيفة. عرّف خريطة routes واستخدم Navigator.pushNamed:
تسجيل المسارات المسمّاة واستخدامها
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/detail': (context) => const DetailScreen(itemId: 0),
'/settings': (context) => const SettingsScreen(),
},
)
// التنقل من أي مكان في التطبيق
Navigator.pushNamed(context, '/detail');
Navigator.push مع MaterialPageRoute للمسارات المجهولة، وNavigator.pushNamed لإعداد متعدد الشاشات أكثر نظافة، وأزِل دائماً مع بيانات اختيارية عندما تحتاج شاشة إلى إرجاع نتيجة إلى مستدعيها.