PopupMenuButton

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) وشرحها بوضوح
  • itemBuilderPopupMenuEntry<T> Function(BuildContext)مطلوبة
    دالة تعيد قائمة العناصر (مثل PopupMenuItem, PopupMenuDivider, CheckedPopupMenuItem). PopupMenuEntry هو النوع العام الذي تمثّل به العناصر داخل القائمة.
  • onSelected(T value) -> void?
    تُستدعى عندما يختار المستخدم عنصرًا. تستلم القيمة value كما مرّرت في PopupMenuItem(value: ...).
  • onCanceledVoidCallback?
    تُستدعى عندما تُغلق القائمة بدون اختيار (مثلاً ضغط المستخدم خارج القائمة).
  • initialValueT?
    القيمة الابتدائية — تستخدم مثلاً عندما تريد عرض عنصر مُعلم كمحدد سابقًا (تؤثر على CheckedPopupMenuItem إن استُخدمت).
  • iconWidget?
    بدل أن يعرض زرًا نصيًا، يمكنك تمرير ويدجت أيقونة (مثلاً Icon(Icons.more_vert)).
  • childWidget?
    بديل عن icon، لو حبيت تقدم ويدجت مخصّص بدل الأيقونة الافتراضية.
  • iconSizedouble
    حجم الأيقونة إن استخدمت icon.
  • paddingEdgeInsetsGeometry
    padding داخل الزر (الذي يظهر في الـUI قبل فتح القائمة).
  • elevationdouble
    ارتفاع الظل (z-coordinate) للقائمة المنبثقة.
  • shapeShapeBorder?
    شكل حدود صندوق القائمة (مثلاً RoundedRectangleBorder لزوايا مدوّرة).
  • colorColor?
    لون خلفية القائمة (الـ background color).
  • offsetOffset
    إزاحة مكان ظهور القائمة (يدفع مكان الpopup بمقدار x,y بالنسبة للموقع الافتراضي). مفيد لضبط الموضع إذا غطي عناصر مهمة.
  • tooltipString?
    نص التلميح (يظهر عند الضغط المطوّل أو في web/desktop كـ tooltip).
  • enabledbool
    إن كان الزر مفعلًا. إذا false سيتصرف كعنصر غير قابل للضغط.
  • enableFeedbackbool
    إن أردت تفعيل/إيقاف تأثير الرجّة (haptic) أو الصوتي عند الضغط على الزر.
  • captureInheritedThemesbool (موجود في بعض إصدارات 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')),
      ],
    ),
  ],
);
PHP
2) استخدام أيقونة مخصصة و 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')),
  ],
);
PHP
3) استخدام 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('اثنان')),
  ],
);
PHP
4) إعادة قيمة للـ 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),
);
PHP
6) مثال عملي في 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 لتحكم أدق.