تمرير المعاملات وسلاسل الاستعلام مع GoRouter
تمرير المعاملات وسلاسل الاستعلام مع GoRouter
من أهم القدرات في أي موجّه (Router) هي إمكانية تمرير البيانات بين الشاشات. يوفر GoRouter ثلاث آليات متكاملة لهذا الغرض: معاملات المسار (مثل /product/:id)، ومعاملات الاستعلام (مثل ?sort=price&page=2)، ومعامل extra لتمرير كائنات Dart الاعتباطية التي لا يمكن ترميزها في سلسلة URL. إتقان الثلاثة يتيح لك بناء تنقل نظيف قابل للربط العميق.
معاملات المسار
معاملات المسار هي مقاطع متغيرة مضمّنة مباشرة في مسار URL، مسبوقة بنقطتين. وهي مثالية للبيانات الإلزامية ذات الطابع التعريفي — كمعرّف منتج، أو اسم مستخدم، أو slug مقالة — لأن المورد لا معنى له بدونها.
تُعرَّف في قالب المسار وتُقرأ من GoRouterState.pathParameters داخل دالة builder:
تعريف معاملات المسار وقراءتها
// تعريف المسار — :id هو معامل المسار
GoRoute(
path: '/product/:id',
builder: (BuildContext context, GoRouterState state) {
// pathParameters هو Map<String, String>
final String productId = state.pathParameters['id']!;
return ProductDetailPage(productId: productId);
},
),
// التنقل بقيمة محددة
context.go('/product/42');
context.go('/product/abc-xyz-789');
String. إذا كان نموذجك يستخدم معرّف int، حوّله صراحةً: int.parse(state.pathParameters['id']!). استخدم int.tryParse بحذر وأعد التوجيه إلى شاشة 404 عندما تكون القيمة مفقودة أو غير رقمية.معاملات مسار متعددة
يمكن أن يحتوي مسار واحد على أكثر من معامل مقطعي. كل مقطع مسبوق بنقطتين يصبح مفتاحاً منفصلاً في pathParameters:
مسار بمعاملات متعددة
GoRoute(
path: '/shop/:category/:productId',
builder: (BuildContext context, GoRouterState state) {
final String category = state.pathParameters['category']!;
final String productId = state.pathParameters['productId']!;
return ProductPage(
category: category,
productId: int.parse(productId),
);
},
),
// التنقل
context.go('/shop/electronics/101');
context.go('/shop/books/978');
معاملات الاستعلام
تظهر معاملات الاستعلام بعد الرمز ? في URL وهي اختيارية تماماً. وهي مثالية للتصفية والفرز والترقيم الصفحي وأعلام الميزات — سياق يُثري الشاشة دون أن يكون جزءاً من هوية المورد.
تُقرأ من GoRouterState.uri.queryParameters التي تُعيد Map<String, String>:
تعريف مسار يقرأ معاملات الاستعلام
GoRoute(
path: '/products',
builder: (BuildContext context, GoRouterState state) {
// queryParameters هو Map<String, String> — جميع القيم اختيارية
final String? sort = state.uri.queryParameters['sort'];
final String? search = state.uri.queryParameters['search'];
final int page = int.tryParse(
state.uri.queryParameters['page'] ?? '',
) ?? 1;
return ProductListPage(
sort: sort,
search: search,
page: page,
);
},
),
// التنقل مع معاملات الاستعلام
context.go('/products?sort=price&page=2');
context.go('/products?search=flutter&sort=rating');
/category/:slug لا يزال يمكنه استقبال معاملات استعلام: context.go('/category/books?sort=newest&page=3'). يتم ملء كل من pathParameters و uri.queryParameters في آنٍ واحد.تمرير الكائنات المعقدة باستخدام extra
لا يمكن لسلاسل URL حمل سوى النص. عندما تحتاج إلى إعادة توجيه كائن Dart كامل — نموذج محمّل مسبقاً، أو قائمة عناصر، أو هيكل إعداد — استخدم معامل extra. يقبل أي Object? ويتنقل عبر مكدس التنقل دون تسلسل URL.
استخدام معامل extra
// 1. تعريف المسار لإعادة تحويل extra إلى نوعه
GoRoute(
path: '/product/:id',
builder: (BuildContext context, GoRouterState state) {
// extra مكتوب كـ Object? — أعد التحويل بحذر
final Product? product = state.extra as Product?;
final String productId = state.pathParameters['id']!;
// إذا كان extra فارغاً (مثل الربط العميق المباشر)، جلب بالمعرّف
if (product != null) {
return ProductDetailPage(product: product);
}
return ProductDetailPage.fromId(productId: productId);
},
),
// 2. تمرير الكائن عند التنقل
final Product selectedProduct = Product(
id: '42',
name: 'Wireless Headphones',
price: 129.99,
);
context.go('/product/42', extra: selectedProduct);
// 3. تمرير Map عند وجود قيم متعددة متفرقة
context.go(
'/checkout',
extra: <String, dynamic>{
'cartItems': cart.items,
'promoCode': 'FLUTTER20',
},
);
extra لا تُحفظ عندما يصل المستخدم مباشرةً عبر URL (مثل كتابة العنوان في المتصفح أو النقر على إشعار دفع). نفّذ دائماً احتياطياً يجلب البيانات بالمعرّف عندما يكون extra بقيمة null. هذا بالغ الأهمية لتطبيقات الويب والتطبيقات التي تدعم الروابط العامة (Universal Links).التنقل بـ GoRouter.push مقابل go
يقبل كل من context.go() و context.push() مجموعة المعاملات الكاملة. استخدم go لاستبدال الموقع الحالي (لا زر رجوع للشاشة السابقة)، و push لإضافة شاشة جديدة فوق المكدس (مع الحفاظ على تنقل الرجوع).
context.go('/product/42', extra: product)— استبدال الموقع الحاليcontext.push('/product/42', extra: product)— دفع فوق المكدسcontext.goNamed('product', pathParameters: {'id': '42'})— التنقل باسم المسارcontext.pushNamed('product', pathParameters: {'id': '42'}, queryParameters: {'tab': 'reviews'})— دفع بمسار مسمّى مع معاملات استعلام
ملخص
يمنحك GoRouter ثلاث قنوات للمعاملات تكمّل بعضها البعض:
- معاملات المسار (
state.pathParameters) — مقاطع URL إلزامية تشكّل هوية المورد مثل/user/:id. - معاملات الاستعلام (
state.uri.queryParameters) — أزواج مفتاح-قيمة اختيارية بعد?، رائعة للفلاتر والترقيم الصفحي. extra— كائنات Dart في الذاكرة، مثالية عندما تمتلك البيانات وتريد تجنب استدعاء شبكي مكرر، لكن دائماً وفّر احتياطياً قائماً على المعرّف لأمان الربط العميق.
extra فقط كاختصار للأداء، وليس أبداً كمصدر الحقيقة الوحيد للشاشة.