PopupMenuButton<T>
هو ويدجت جاهز في Flutter يعرض قائمة منبثقة (popup menu) عندما يضغط المستخدم على الزر/الأيقونة.
كل بند (PopupMenuItem) يمكن أن يحمل قيمة من النوع T وتُعاد هذه القيمة عند اختيار المستخدم. – – مناسب لعرض خيارات سريعة مثل “مشاركة / حذف / تعديل” بدون فتح شاشة جديدة.
الفكرة الأساسية (بأسلوب مبسّط)
- تضع
PopupMenuButtonفي مكان الواجهة (عادة فيAppBarأو بجانب أزرار). - تزوّدها بـ
itemBuilderالذي يرجع قائمة عناصر - (
List<PopupMenuEntry<T>>) — فهذه العناصر ستكون الموجودة داخل القائمة. - عند اختيار عنصر، تستدعى
onSelectedبـقيمة ذلك العنصر.
الشكل العام (syntax)
PopupMenuButton<T>(
itemBuilder: (BuildContext context) => <PopupMenuEntry<T>>[
PopupMenuItem<T>(value: value1, child: Text('Option 1')),
PopupMenuItem<T>(value: value2, child: Text('Option 2')),
],
onSelected: (T value) { /* handle selection */ },
)PHPأهم الخصائص (properties) وشرحها بوضوح
itemBuilder—PopupMenuEntry<T> Function(BuildContext)— مطلوبة
دالة تعيد قائمة العناصر (مثلPopupMenuItem,PopupMenuDivider,CheckedPopupMenuItem).PopupMenuEntryهو النوع العام الذي تمثّل به العناصر داخل القائمة.onSelected—(T value) -> void?
تُستدعى عندما يختار المستخدم عنصرًا. تستلم القيمةvalueكما مرّرت فيPopupMenuItem(value: ...).onCanceled—VoidCallback?
تُستدعى عندما تُغلق القائمة بدون اختيار (مثلاً ضغط المستخدم خارج القائمة).initialValue—T?
القيمة الابتدائية — تستخدم مثلاً عندما تريد عرض عنصر مُعلم كمحدد سابقًا (تؤثر علىCheckedPopupMenuItemإن استُخدمت).icon—Widget?
بدل أن يعرض زرًا نصيًا، يمكنك تمرير ويدجت أيقونة (مثلاًIcon(Icons.more_vert)).child—Widget?
بديل عنicon، لو حبيت تقدم ويدجت مخصّص بدل الأيقونة الافتراضية.iconSize—double
حجم الأيقونة إن استخدمتicon.padding—EdgeInsetsGeometry
padding داخل الزر (الذي يظهر في الـUI قبل فتح القائمة).elevation—double
ارتفاع الظل (z-coordinate) للقائمة المنبثقة.shape—ShapeBorder?
شكل حدود صندوق القائمة (مثلاًRoundedRectangleBorderلزوايا مدوّرة).color—Color?
لون خلفية القائمة (الـ background color).offset—Offset
إزاحة مكان ظهور القائمة (يدفع مكان الpopup بمقدارx,yبالنسبة للموقع الافتراضي). مفيد لضبط الموضع إذا غطي عناصر مهمة.tooltip—String?
نص التلميح (يظهر عند الضغط المطوّل أو في web/desktop كـ tooltip).enabled—bool
إن كان الزر مفعلًا. إذاfalseسيتصرف كعنصر غير قابل للضغط.enableFeedback—bool
إن أردت تفعيل/إيقاف تأثير الرجّة (haptic) أو الصوتي عند الضغط على الزر.captureInheritedThemes—bool(موجود في بعض إصدارات Flutter)
يحدد ما إذا كان Popup يجب أن يرث الثيم من الـcontext الملتقط أم لا.
ملاحظة: بعض المسميات الدقيقة والخصائص الإضافية قد تختلف بالإصدارات الأحدث من Flutter — لكن ما سبق يغطي الخصائص الأكثر استخدامًا.
أنواع العناصر داخل القائمة (PopupMenuEntry)
PopupMenuItem<T>— العنصر الأساسي. لهvalueوchild.
مثال:PopupMenuItem<int>(value: 1, child: Text('حذف')).CheckedPopupMenuItem<T>— عنصر مع علامة صح (checked) لتمثيل خيارات مختارة. يأخذchecked: bool.PopupMenuDivider— فاصل خطي يتم وضعه بين العناصر.- يمكن أيضًا وضع أي
PopupMenuEntryمخصّص لكن عادةً نستخدم الثلاثة أعلاه.
أمثلة عملية
1) أبسط مثال — أيقونة في AppBar
AppBar(
title: Text('Example'),
actions: [
PopupMenuButton<String>(
onSelected: (value) {
print('Selected: $value');
},
itemBuilder: (context) => [
PopupMenuItem(value: 'edit', child: Text('Edit')),
PopupMenuItem(value: 'delete', child: Text('Delete')),
],
),
],
);PHP2) استخدام أيقونة مخصصة و tooltip
PopupMenuButton<int>(
icon: Icon(Icons.more_vert),
tooltip: 'الخيارات',
onSelected: (i) => print('Chosen $i'),
itemBuilder: (_) => [
PopupMenuItem(value: 1, child: Text('خيار 1')),
PopupMenuItem(value: 2, child: Text('خيار 2')),
],
);PHP3) استخدام CheckedPopupMenuItem و PopupMenuDivider
PopupMenuButton<String>(
initialValue: 'two',
onSelected: (v) => print(v),
itemBuilder: (context) => [
CheckedPopupMenuItem(value: 'one', checked: false, child: Text('واحد')),
PopupMenuDivider(),
CheckedPopupMenuItem(value: 'two', checked: true, child: Text('اثنان')),
],
);PHP4) إعادة قيمة للـ caller وانتظارها (await)
// استدعاء من داخل onPressed
final result = await showMenu<String>(
context: context,
position: RelativeRect.fromLTRB(100, 100, 0, 0),
items: [
PopupMenuItem(value: 'a', child: Text('A')),
PopupMenuItem(value: 'b', child: Text('B')),
],
);
// showMenu مشابه لـ PopupMenu لكن يعيد Future مباشرة.
// لكن مع PopupMenuButton يمكنك استخدام onSelected أو الاستماع عبر state.PHP(ملاحظة: showMenu تعتبر بديلًا عندما تريد التحكم الكامل بالموضع والعودة بقيمة مباشرة).
5) عناصر مخصّصة (Widget داخل PopupMenuItem)
PopupMenuButton<int>(
itemBuilder: (_) => [
PopupMenuItem(
value: 1,
child: Row(
children: [
Icon(Icons.share),
SizedBox(width: 8),
Text('Share'),
],
),
),
PopupMenuItem(value: 2, child: Text('Settings')),
],
onSelected: (v) => print(v),
);PHP6) مثال عملي في Stateful لتحديث اختيار
class MyWidget extends StatefulWidget { /*...*/ }
class _MyWidgetState extends State<MyWidget> {
String selected = 'none';
@override
Widget build(BuildContext context) {
return Row(
children: [
Text('Selected: $selected'),
PopupMenuButton<String>(
onSelected: (v) => setState(() => selected = v),
itemBuilder: (_) => [
PopupMenuItem(value: 'one', child: Text('One')),
PopupMenuItem(value: 'two', child: Text('Two')),
],
),
],
);
}
}PHPنصائح وممارسات جيدة
- إذا القائمة كبيرة (عدد عناصر كثير) فكّر في استخدام
showMenuمعpositionأو تصميم قائمةDialogبدل popup تقليدي. - استخدم
valueمن نوعenumبدلStringللحفاظ على صفاء الكود ووقاية من الأخطاء. مثال:enum MenuAction { edit, delete } PopupMenuButton<MenuAction>( /* ... */ ); - خصّص
shapeوcolorإذا أردت مظهرًا مطابقًا للثيم العام للتطبيق. - لتفادي اختفاء القائمة فوق عناصر مهمة، جرّب ضبط
offset.
أخطاء شائعة وكيفية إصلاحها
- لا تظهر القائمة عند الضغط: تأكد أن
enabled: true(افتراضيًا true) وأنitemBuilderيعيد قائمة غير فارغة. - لا يتم التعامل مع الاختيار: ضع
onSelectedأو تحقق منvalueفيPopupMenuItem. - موقع القائمة خاطئ على شاشات كبيرة: استخدم
offsetأوshowMenuمعpositionلتحكم أدق.