التنقل بالقائمة الجانبية
التنقل بالقائمة الجانبية في Flutter
القائمة الجانبية (Drawer) هي لوحة جانبية تنزلق من الحافة اليسرى (أو اليمنى) للشاشة، وتوفر وصولاً إلى وجهات التنقل الرئيسية في تطبيقك. وهي نمط تصميم Material يعمل بشكل ممتاز على الأجهزة المحمولة حيث تكون مساحة الشاشة محدودة. يجعل Flutter من السهل إضافة قائمة جانبية كاملة الوظائف إلى أي Scaffold عن طريق تعيين خاصية drawer الخاصة به.
تُعدّ القوائم الجانبية مثالية عندما يحتوي تطبيقك على خمس وجهات رئيسية أو أكثر قد تُزدحم بها شريط التنقل السفلي، أو عندما تريد إبقاء منطقة المحتوى الرئيسية نظيفة وخالية من التشتيت. يفتح المستخدم القائمة الجانبية بالتمرير من الحافة اليسرى للشاشة أو بالنقر على أيقونة القائمة (الهامبرغر) التي يضعها Flutter تلقائياً في AppBar.
AppBar عند توفير drawer للـ Scaffold. لا تحتاج إلى إضافة الأيقونة يدوياً. استخدم endDrawer بدلاً من drawer إذا أردت أن تنزلق اللوحة من اليمين.هيكل القائمة الجانبية الأساسي
تُبنى القائمة الجانبية باستخدام ودجت Drawer الذي يُغلّف عادةً ListView تحتوي على DrawerHeader (أو UserAccountsDrawerHeader) يتبعه سلسلة من ودجات ListTile — واحد لكل وجهة تنقل.
مثال بسيط على القائمة الجانبية
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/profile': (context) => const ProfileScreen(),
'/settings': (context) => const SettingsScreen(),
},
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('الرئيسية')),
drawer: Drawer(
child: ListView(
// إزالة الحشو العلوي الافتراضي لتجلس DrawerHeader بشكل مستوٍ
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(color: Colors.indigo),
child: Text(
'تطبيقي',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
ListTile(
leading: const Icon(Icons.home),
title: const Text('الرئيسية'),
onTap: () {
Navigator.pop(context); // أغلق القائمة أولاً
Navigator.pushReplacementNamed(context, '/');
},
),
ListTile(
leading: const Icon(Icons.person),
title: const Text('الملف الشخصي'),
onTap: () {
Navigator.pop(context);
Navigator.pushReplacementNamed(context, '/profile');
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('الإعدادات'),
onTap: () {
Navigator.pop(context);
Navigator.pushReplacementNamed(context, '/settings');
},
),
],
),
),
body: const Center(child: Text('شاشة الرئيسية')),
);
}
}
Navigator.pop(context) قبل التنقل لإغلاق القائمة الجانبية بسلاسة. إذا تنقلت دون إغلاقها، ستبقى القائمة فوق الشاشة الجديدة حتى يرفضها المستخدم يدوياً.تمييز الوجهة النشطة
تجربة المستخدم الجيدة تتطلب أن تُشير القائمة الجانبية بصرياً إلى الشاشة التي يتواجد فيها المستخدم حالياً. يمكنك تحقيق ذلك عن طريق تتبع فهرس محدد (أو اسم المسار المحدد) في StatefulWidget واستخدام خاصية selected الخاصة بـ ListTile مع selectedTileColor لتمييز العنصر النشط.
قائمة جانبية مع تمييز العنصر النشط
class MainScaffold extends StatefulWidget {
const MainScaffold({super.key});
@override
State<MainScaffold> createState() => _MainScaffoldState();
}
class _MainScaffoldState extends State<MainScaffold> {
// تتبع الوجهة النشطة حالياً
int _selectedIndex = 0;
// قوائم متوازية: الشاشات وبيانات القائمة الجانبية
final List<Widget> _screens = const [
HomeScreen(),
ProfileScreen(),
SettingsScreen(),
];
final List<Map<String, dynamic>> _navItems = const [
{'icon': Icons.home, 'label': 'الرئيسية'},
{'icon': Icons.person, 'label': 'الملف الشخصي'},
{'icon': Icons.settings, 'label': 'الإعدادات'},
];
void _navigateTo(int index) {
setState(() {
_selectedIndex = index;
});
Navigator.pop(context); // أغلق القائمة
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_navItems[_selectedIndex]['label'] as String),
backgroundColor: Colors.indigo,
foregroundColor: Colors.white,
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
UserAccountsDrawerHeader(
accountName: const Text('إدريس صالح'),
accountEmail: const Text('edrees@example.com'),
currentAccountPicture: const CircleAvatar(
backgroundColor: Colors.white,
child: Icon(Icons.person, color: Colors.indigo, size: 40),
),
decoration: const BoxDecoration(color: Colors.indigo),
),
...List.generate(_navItems.length, (index) {
final item = _navItems[index];
return ListTile(
leading: Icon(item['icon'] as IconData),
title: Text(item['label'] as String),
selected: _selectedIndex == index,
selectedTileColor: Colors.indigo.withOpacity(0.12),
selectedColor: Colors.indigo,
onTap: () => _navigateTo(index),
);
}),
const Divider(),
ListTile(
leading: const Icon(Icons.logout),
title: const Text('تسجيل الخروج'),
onTap: () {
Navigator.pop(context);
// منطق تسجيل الخروج هنا
},
),
],
),
),
body: _screens[_selectedIndex],
);
}
}
UserAccountsDrawerHeader
ودجت UserAccountsDrawerHeader هو رأس قائمة جانبية وفق معايير Material مصمم لعرض صورة المستخدم الرمزية واسمه وعنوان بريده الإلكتروني. يتعامل تلقائياً مع التفاعل بالنقر للتبديل بين الحسابات ويتكيف مع سمة التطبيق. تشمل الخصائص الرئيسية:
accountName— ودجت اسم المستخدم الأساسيaccountEmail— ودجت البريد الإلكتروني أو النص الثانويcurrentAccountPicture— عادةًCircleAvatardecoration— خلفية أو تدرج أو صورة للرأسotherAccountsPictures— صور رمزية صغيرة لحسابات إضافية
فتح وإغلاق القائمة الجانبية برمجياً
أحياناً تحتاج إلى فتح القائمة الجانبية أو إغلاقها من الكود بدلاً من تفاعل المستخدم. استخدم GlobalKey<ScaffoldState> للحصول على مرجع للـ Scaffold واستدعاء openDrawer() أو closeDrawer().
التحكم البرمجي في القائمة الجانبية
class MyPage extends StatelessWidget {
// تعيين مفتاح للـ Scaffold
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
MyPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('لوحة التحكم'),
// زر مخصص لفتح القائمة الجانبية
leading: IconButton(
icon: const Icon(Icons.menu),
onPressed: () => _scaffoldKey.currentState?.openDrawer(),
),
),
drawer: const Drawer(
child: Center(child: Text('التنقل هنا')),
),
body: Center(
child: ElevatedButton(
onPressed: () => _scaffoldKey.currentState?.openDrawer(),
child: const Text('فتح القائمة'),
),
),
);
}
}
leading مخصصاً وdrawer معاً للـ Scaffold دون تعيين automaticallyImplyLeading: false إذا أردت التحكم الكامل. بشكل افتراضي، سيعرض Flutter أيقونة الهامبرغر تلقائياً وودجت leading المخصص الخاص بك سيحل محلها.عرض وشكل القائمة الجانبية
العرض الافتراضي للقائمة الجانبية هو 304 بكسل منطقي (وفق إرشادات Material). يمكنك تخصيص المظهر من خلال خصائص Drawer:
width— تعيين عرض مخصص (مثل280)backgroundColor— لون خلفية لوحة القائمة الجانبيةshape— زوايا مدورة أو أشكال حدود أخرىelevation— عمق الظل (الافتراضي 16)
ملخص
تقبل خاصية drawer في Scaffold أي ودجت، لكن النمط التقليدي هو Drawer → ListView (مع padding: EdgeInsets.zero) → ودجت رأس + عناصر ListTile. تتبع فهرس الوجهة المحددة في StatefulWidget، ومرره إلى خاصية selected لكل ListTile، واستدع Navigator.pop(context) قبل التنقل لمنح تجربة مستخدم سلسة. للتحكم البرمجي، استخدم GlobalKey<ScaffoldState>.