SnackBar

ما هو SnackBar؟

SnackBar هو ويدجت من مكتبة Material في Flutter لعرض رسالة خفيفة مؤقتة في أسفل الشاشة، غالبًا مع زر إجراء اختياري (مثلاً “Undo”). الهدف: إعلام المستخدم بسرعة دون إيقاف تفاعله مع التطبيق. Flutter API Docs+1

كيف أُظهر SnackBar (الطريقة الموصى بها)

إنشئ SnackBar ثم أعرضه عبر ScaffoldMessenger:

final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
PHP
     ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                padding: EdgeInsets.all(50),
                action: SnackBarAction(label: "label", onPressed:(){}),
                duration: Duration(seconds: 10),
                showCloseIcon: true,
                dismissDirection: DismissDirection.endToStart,
                backgroundColor: Colors.red,
                behavior: SnackBarBehavior.floating,
                margin: EdgeInsets.all(50),
                closeIconColor: Colors.amber,
                content: Text("data")));
PHP

هذا هو الأسلوب الرسمي الآن (لم يعد يُنصح باستخدام Scaffold.of(context).showSnackBar) ولديك خيار استخدام مفتاح عام (scaffoldMessengerKey) في MaterialApp إذا أردت التحكم من خارج الـBuildContext. Flutter Docs+1

الخصائص (properties) المهمة — شرح مرتب للمبتدئ

(الإشارة: هذه الخصائص هي معاملات منشئ SnackBar كما في توثيق Flutter). Flutter API Docs

  • content (مطلوب)
    الـ widget الذي يظهر داخل الـSnackBar — عادةً Text(...). Flutter API Docs
  • action (SnackBarAction?)
    زر واحد اختياري يظهر بجانب الرسالة (مثال: label: 'Undo', onPressed: () { ... }). يمكن تخصيص لون النص عبر textColor. Flutter API Docs+1
  • duration (Duration)
    المدة التي يبقى فيها الـSnackBar ظاهرًا — الافتراضي هو 4.0 ثواني. ملاحظة: إذا كان هناك action فقد يتصرف الوصول (accessibility) بحيث لا يُغلق تلقائيًا على بعض إعدادات المساعد الصوتي. Flutter API Docs+1
  • behavior (SnackBarBehavior)
    يحدّد موضع وسلوك الـSnackBar: fixed (ملتصق بعرض الشاشة) أو floating (مستقل، يسمح بـmargin وshape). الافتراضي fixed. استخدم floating لتصميم أصغر مع زوايا مستديرة ومصاريف حوله. Flutter API Docs
  • margin, padding, width, shape, elevation, backgroundColor
    عناصر تخصيص الشكل والمكان والظل. ملاحظة: margin يُطبق فقط عندما تكون behavior = SnackBarBehavior.floating، وwidth وmargin لا يمكن استخدامهما معًا (assert في البناء). elevation يؤثر على الظل، خصوصًا عند floating. Flutter API Docs+1
  • padding (قيمة افتراضية مفصّلة)
    إذا لم تُعطَ padding، فهناك قواعد افتراضية (مثلاً: top/bottom = 14; left = 24 عندما fixed و16 عندما floating; وright = left إذا لم يكن هناك action، وإلا 0). هذه التفاصيل مهمة عند تصميم النص وطريقة لفّه. Flutter API Docs
  • actionOverflowThreshold (double بين 0 و1)
    تحدد متى يُنقل زر الـ action إلى سطر جديد إذا كان طول المحتوى كبيرًا. القيمة الافتراضية = 0.25 (أي إذا شغل المحتوى أكثر من 25% من المساحة المتبقية اختار وضع الزر بأسفل). Flutter API Docs
  • showCloseIcon و closeIconColor
    خيار لعرض أيقونة إغلاق صغيرة داخل الـSnackBar مع تخصيص لونها. Flutter API Docs
  • onVisible و animation
    onVisible يتم استدعاؤها عند الظهور لأول مرة — مفيدة لتنفيذ لوجيك وقت العرض. يمكنك أيضاً تمرير animation مخصّصة إذا أردت التحكم بمدخل/خروج الظهور. Flutter API Docs
  • dismissDirection (DismissDirection)
    تحديد اتجاه السحب الذي يسمح للمستخدم بإغلاق الـSnackBar (مثلاً down, horizontal, none…). الافتراضي إذا لم تُعرّف فهو DismissDirection.down. Flutter API Docs
  • hitTestBehavior
    يحدد كيف تتعامل المنطقة (بما في ذلك المسافات الخارجية margin) مع اللمسات/النقرات — قيم مثل deferToChild, opaque, translucent. هذا مهم إذا كان الـSnackBar يغطّي عناصر قابلة للنقر. Flutter API Docs

إدارة الإغلاق ونتائج الإغلاق

عند استدعاء showSnackBar تحصل على ScaffoldFeatureController<SnackBar, SnackBarClosedReason>، يمكنك استخدامه لـ:

  • .close() أو ScaffoldMessenger.of(context).hideCurrentSnackBar() لإغلاق مع أنيميشن خروج.
  • removeCurrentSnackBar() لإزالة فوري بدون أنيميشن.
  • الاستماع إلى controller.closed لمعرفة سبب الإغلاق (SnackBarClosedReason) (مثلاً: action، timeout, dismiss). Flutter API Docs+1
أمثلة عملية (جاهزة للنسخ)

1) أبسط مثال:

ElevatedButton(
  onPressed: () {
    final snackBar = SnackBar(content: Text('تم الحفظ!'));
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  },
  child: Text('أظهر SnackBar'),
)
PHP

(مأخوذ من وصف الاستخدام في Flutter cookbook). Flutter Docs

2) مع إجراء Undo والانتظار لمعرفة سبب الإغلاق (مفيد للتراجع عن حذف):

final controller = ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(
    content: Text('تم حذف العنصر'),
    action: SnackBarAction(label: 'Undo', onPressed: () {
      // فعل التراجع هنا
    }),
    duration: Duration(seconds: 4),
  ),
);

// معرفة نتيجة الإغلاق (مثلاً commit الحذف إذا لم يضغط المستخدم Undo)
controller.closed.then((reason) {
  if (reason != SnackBarClosedReason.action) {
    // لا يوجد تراجع — قم بتطبيق الحذف النهائي
  }
});
PHP

(تذكّر: showSnackBar يرجع ScaffoldFeatureController مع مستقبل closed). Flutter API Docs+1

3) SnackBar مخصّص عائم (floating) بشكل مستدير وزر إغلاق:

final snack = SnackBar(
  content: Row(
    children: [
      Icon(Icons.info_outline),
      SizedBox(width: 8),
      Expanded(child: Text('رسالة مهمة قصيرة')),
    ],
  ),
  behavior: SnackBarBehavior.floating,
  margin: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
  backgroundColor: Colors.blueAccent,
  showCloseIcon: true,
  closeIconColor: Colors.white,
  action: SnackBarAction(
    label: 'حسناً',
    onPressed: () {},
  ),
);
ScaffoldMessenger.of(context).showSnackBar(snack);
PHP

(ملاحظة: margin يعمل فقط مع behavior = floating). Flutter API Docs+1

ثيم/تخصيص عام (Theme)

لو تريد نمطًا موحّدًا لكل الـSnackBars في التطبيق، استخدم SnackBarThemeData داخل ThemeData (مثلاً تغيير backgroundColor, actionTextColor, behavior, actionOverflowThreshold, dismissDirection…) — هذا يسهل المحافظة على تناسق الواجهات. Flutter API Docs

نصائح عملية / أفضل ممارسات للمبتدئين
  • اجعل الرسائل قصيرة وواضحة (سطر واحد أو سطرين كحد أقصى). (مبدأ تصميم Material للـSnackbars). Material Design
  • استخدم زر action فقط لإجراءات سريعة قابلة للتراجع (مثل Undo)، وليس لإجراءات معقّدة. Flutter API Docs
  • إن أردت إشعارًا أكثر أهمية أو تفاعلاً مع خيارات متعددة استخدم Dialog أو BottomSheet بدل SnackBar.
  • لا تضع SnackBar فوق عناصر مهمة دون Margin/behavior مناسب حتى لا يحجب عناصر UI قابلة للتفاعل. Flutter API Docs+1