توليد المسارات واستخدام onGenerateRoute
توليد المسارات واستخدام onGenerateRoute
مع نمو تطبيقات Flutter، يصبح الاحتفاظ بخريطة routes ثابتة داخل MaterialApp أمراً صعباً. كل شاشة جديدة تتطلب إدخالاً إضافياً، ويكون تمرير المعاملات المعقدة عبر أسماء المسارات أمراً محرجاً. يحل Flutter هذا بـ onGenerateRoute — دالة استدعاء واحدة تعترض كل طلب تنقل يعتمد على الاسم وتبني المسار Route المناسب ديناميكياً. يُمركز هذا النهج منطق التوجيه بالكامل في مكان واحد ويجعل استخراج المعاملات نظيفاً وآمناً من حيث الأنواع.
قيود خريطة routes الثابتة
تقبل خريطة routes الثابتة نوع Map<String, WidgetBuilder> بسيط. كل منشئ يستقبل BuildContext فقط، مما يعني:
- لا يمكنك الوصول إلى المعاملات الممررة مع
Navigator.pushNamed(context, '/detail', arguments: item) - يجب تضمين كل مسار في وقت الترجمة — لا توجد أنماط شرائح ديناميكية مثل
/user/:id - لا يوجد مكان واحد لإضافة اهتمامات مشتركة مثل حراسة المصادقة أو التحليلات
- المسارات المجهولة تتعطل بصمت بدلاً من الوصول إلى شاشة خطأ مخصصة
routes وonGenerateRoute. يتحقق Flutter من routes أولاً؛ إذا لم يُعثر على اسم المسار هناك، فإنه يمر إلى onGenerateRoute. استخدم routes فقط للشاشات الأبسط التي لا تحتاج معاملات، ودع onGenerateRoute يتعامل مع كل شيء آخر.دالة الاستدعاء onGenerateRoute
onGenerateRoute هي خاصية في MaterialApp (وCupertinoApp) تقبل دالة بالتوقيع Route<dynamic>? Function(RouteSettings settings). يحمل كائن RouteSettings خاصيتين:
settings.name— سلسلة اسم المسار (مثل'/product/detail')settings.arguments— القيمة الممررة كـargumentsفي استدعاء الدفع، بنوعObject?
تحلل settings.name بعبارة switch (أو سلسلة if-else)، وتحوّل settings.arguments إلى النوع المتوقع، وتعيد MaterialPageRoute يلف ودجت الشاشة الخاصة بك.
تبديل onGenerateRoute الأساسي
// main.dart
MaterialApp(
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (_) => const HomeScreen(),
settings: settings,
);
case '/product/detail':
// تحويل المعاملات إلى النوع المتوقع
final product = settings.arguments as Product;
return MaterialPageRoute(
builder: (_) => ProductDetailScreen(product: product),
settings: settings,
);
case '/user/profile':
final userId = settings.arguments as String;
return MaterialPageRoute(
builder: (_) => UserProfileScreen(userId: userId),
settings: settings,
);
default:
// أعد null للتفويض إلى onUnknownRoute
return null;
}
},
);
settings: settings إلى MaterialPageRoute. يحفظ هذا بيانات تعريف المسار للتحليلات وفحص مكدس الرجوع وواجهة برمجة التطبيقات RouteObserver. بدونه، تُعاد تعيين خاصية settings للمسار إلى RouteSettings فارغ جديد.التعامل مع المسارات المجهولة باستخدام onUnknownRoute
عندما يُرجع onGenerateRoute قيمة null (أو لم يُعرَّف)، يستدعي Flutter onUnknownRoute كملاذ أخير. هذا هو المكان الصحيح لعرض شاشة بنمط 404 بدلاً من السماح للتطبيق بالانهيار. يمتلك onUnknownRoute نفس توقيع onGenerateRoute لكنه يجب أن يُرجع مساراً غير null — يطرح Flutter خطأ تأكيد إذا أرجع null.
الجمع بين onGenerateRoute و onUnknownRoute
MaterialApp(
initialRoute: '/',
onGenerateRoute: _generateRoute,
onUnknownRoute: (RouteSettings settings) {
// أرجع دائماً مساراً صحيحاً هنا — لا تُرجع null أبداً
return MaterialPageRoute(
builder: (_) => NotFoundScreen(routeName: settings.name ?? 'unknown'),
settings: settings,
);
},
);
// منفصل في دالة خاصة به لسهولة القراءة
Route<dynamic>? _generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => const HomeScreen(), settings: settings);
case '/settings':
return MaterialPageRoute(builder: (_) => const SettingsScreen(), settings: settings);
case '/order':
final args = settings.arguments as Map<String, dynamic>;
return MaterialPageRoute(
builder: (_) => OrderScreen(
orderId: args['orderId'] as String,
isPriority: args['isPriority'] as bool? ?? false,
),
settings: settings,
);
default:
return null; // يُفوِّض إلى onUnknownRoute
}
}
استخراج المعاملات بأمان
تحويل settings.arguments مباشرة بـ as سيطرح TypeError في وقت التشغيل إذا نسي المُستدعي تمرير المعاملات. استخدم تحويلاً شرطياً (as? أو فحص is) لتوفير قيم افتراضية معقولة:
- استخدم
settings.arguments as MyType?وقدم قيمة افتراضية بـ?? - فضّل تمرير فئة معاملات مكتوبة بشكل قوي بدلاً من
Mapخام - تحقق من المعاملات مبكراً داخل منشئ المسار حتى تظهر الأخطاء بوضوح
تنظيم المسارات في ملف منفصل
بالنسبة للتطبيقات الكبيرة، استخرج _generateRoute في فئة AppRouter خاصة بها. يبقي هذا ملف main.dart نظيفاً ويجعل اختبار منطق التوجيه وحدةً أمراً مباشراً.
Navigator.push (المستند إلى الودجت) وNavigator.pushNamed (المستند إلى الاسم) بشكل عشوائي. اختر استراتيجية واحدة لكل قسم من تطبيقك. خلطهما يجعل من المستحيل مركزة حراسة المسارات في onGenerateRoute لأن الدفعات المستندة إلى الودجت تتجاوزه تماماً.الخلاصة
onGenerateRoute هو البديل الموصى به لخريطة routes المسطحة في أي تطبيق يحتاج إلى تمرير معاملات أو حراسة مسارات أو التعامل مع المسارات المجهولة بشكل أنيق. النمط بسيط: استقبل RouteSettings، طابق على الاسم، استخرج معاملات مكتوبة، وأرجع MaterialPageRoute. اقرنه بـ onUnknownRoute لتوفير شاشة احتياطية مخصصة، واستخرج دالة الاستدعاء إلى فئة موجّه مخصصة مع نمو التطبيق.