كيفية إعداد الإشعارات المحلية في Flutter باحتراف
مقدمة: لماذا تعد الإشعارات المحلية مهمة في تطبيقات Flutter؟
تُعد الإشعارات وسيلة فعالة لإبقاء المستخدم على اتصال دائم بالتطبيق، سواء لتذكيره بمهمة مهمة، أو لتنبيهه بحدث داخلي، أو لإعادته إلى التطبيق في الوقت المناسب. وفي تطبيقات Flutter، يمكن الاعتماد على الإشعارات المحلية لبناء تجربة استخدام أكثر تفاعلاً دون الحاجة إلى خادم خارجي.
تنقسم الإشعارات عادة إلى نوعين رئيسيين:
- إشعارات الدفع
Push Notifications. - الإشعارات المحلية
Local Notifications.
الفرق الجوهري بينهما أن الإشعارات المحلية تصدر من داخل التطبيق نفسه، بينما يتم إرسال إشعارات الدفع من خادم خارجي. في هذا الدليل سنركز حصراً على إعداد الإشعارات المحلية داخل Flutter بطريقة عملية ومناسبة للتطبيقات الحقيقية.

فهم آلية عمل الإشعارات المحلية
قبل البدء في التنفيذ، من المهم معرفة أن مكتبة flutter_local_notifications تتعامل مع كل من Android وiOS، لكن طريقة عمل الإشعارات تختلف بين النظامين. لذلك ستحتاج إلى تهيئة خاصة بكل منصة، ثم توحيد الاستخدام من خلال طبقة خدمة داخل التطبيق.
أفضل ممارسة هنا هي إنشاء ملف خدمة مستقل مثل notification_service.dart بحيث يتولى:
- تهيئة المكتبة عند تشغيل التطبيق.
- إظهار الإشعارات الفورية.
- جدولة الإشعارات المستقبلية.
- إلغاء إشعار محدد أو جميع الإشعارات.
إعداد المشروع وإضافة الحزمة
لكي يتمكن التطبيق من استخدام الإشعارات المحلية، يجب إضافة حزمة flutter_local_notifications إلى ملف pubspec.yaml ضمن قسم dependencies:
dependencies:
flutter:
sdk: flutter
flutter_local_notifications: ^5.0.0+1
بعد ذلك شغّل أمر جلب الحزم:
flutter pub get
يفضّل بعد تثبيت الحزمة أن تنشئ خدمة مخصصة لإدارة الإشعارات في جميع أجزاء التطبيق.
إنشاء خدمة الإشعارات بنمط Singleton
أنشئ ملفاً جديداً باسم notification_service.dart وضع فيه الكود التالي:
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationService {
static final NotificationService _notificationService = NotificationService._internal();
factory NotificationService() {
return _notificationService;
}
NotificationService._internal();
}
هذا النمط يضمن وجود نسخة واحدة فقط من خدمة الإشعارات داخل التطبيق، وهو أسلوب مناسب لإدارة الموارد والتهيئة المشتركة.
تهيئة مكتبة الإشعارات في Flutter
في البداية نحتاج إلى إنشاء كائن من FlutterLocalNotificationsPlugin لأنه سيكون المسؤول عن التهيئة والإرسال والجدولة والإلغاء:
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
بعد ذلك نُنشئ إعدادات التهيئة عبر الكائن InitializationSettings، والذي يستقبل إعدادات خاصة بكل منصة مثل Android وiOS.
تهيئة الإشعارات على Android
إعداد Android بسيط نسبياً. أهم عنصر مطلوب هو الأيقونة الافتراضية التي ستظهر مع الإشعار. يجب وضع الأيقونة داخل المسار التالي:
YOUR_APPLICATION_NAME/android/app/src/main/res/drawable/YOUR_APP_ICON.png

بعدها يمكن استخدام اسم الأيقونة داخل الإعدادات، مثل app_icon. وفي أغلب الحالات لا تحتاج إلى طلب أذونات خاصة على Android بالطريقة التقليدية المستخدمة في iOS.
تهيئة الإشعارات على iOS
في iOS تحتاج العملية إلى بعض الضبط الإضافي، لأن النظام يتعامل بحساسية أكبر مع الأذونات وسلوك الإشعارات أثناء عمل التطبيق في الواجهة أو الخلفية.
داخل ملف AppDelegate أضف التهيئة التالية:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
if (@available(iOS 10.0, *)) {
[UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate>) self;
}
}
كما أن iOS يتطلب عادة طلب أذونات من المستخدم، مثل:
requestAlertPermissionلعرض التنبيهات.requestBadgePermissionلتحديث شارة التطبيق.requestSoundPermissionلتشغيل الصوت.
ويمكنك أيضاً التحكم في القيم الافتراضية لعرض هذه العناصر من خلال:
defaultPresentAlertdefaultPresentBadgedefaultPresentSound
إذا كنت لا تريد أن تظهر نوافذ طلب الأذونات مباشرة أثناء التهيئة، يمكنك ضبط هذه القيم بما يناسب تدفق تجربة المستخدم داخل التطبيق.
هناك نقطة مهمة أيضاً: في بعض إصدارات iOS لن تُعرض الإشعارات تلقائياً عندما يكون التطبيق مفتوحاً في الواجهة الأمامية. لذلك يمكن استخدام المعالج onDidReceiveLocalNotification للتعامل مع سلوك الإشعار وتفاعل المستخدم معه، خصوصاً في الإصدارات الأقدم من iOS 10.
إنشاء دالة init() لتهيئة الإشعارات
أفضل تنظيم هو وضع كل خطوات التهيئة داخل دالة init() داخل خدمة الإشعارات، ثم استدعاؤها مرة واحدة عند تشغيل التطبيق:
void init() {
final AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: null,
);
}
لكن هذه الخطوة وحدها لا تكفي. بعد إنشاء إعدادات التهيئة، يجب تمريرها إلى الدالة initialize() الخاصة بالكائن flutterLocalNotificationsPlugin.
يمكنك تنفيذ التهيئة الكاملة كما يلي:
Future<void> init() async {
final AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: null,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onSelectNotification: selectNotification,
);
}
Future selectNotification(String payload) async {
// Handle notification tapped logic here
}
المعامل onSelectNotification اختياري، لكنه مهم إذا كنت تريد تنفيذ إجراء معين عند نقر المستخدم على الإشعار، مثل فتح شاشة محددة أو تحميل بيانات مرتبطة بالقيمة payload.
استدعاء التهيئة من ملف main.dart
في ملف main.dart استدعِ الخدمة قبل تشغيل التطبيق:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await NotificationService().init();
runApp(MyApp());
}
هذا يضمن أن كل إعدادات الإشعارات أصبحت جاهزة منذ لحظة الإقلاع الأولى.
حالات الاستخدام الأساسية للإشعارات المحلية
1) كيفية عرض إشعار محلي فوري
لإظهار إشعار، تحتاج أولاً إلى إنشاء كائن تفاصيل مناسب لكل منصة. على Android يمكنك استخدام AndroidNotificationDetails كما يلي:
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
channelId,
channelName,
channelDescription,
importance: Importance.max,
priority: Priority.high,
);
في الإصدارات الحديثة من Android، تعد القناة Notification Channel عنصراً أساسياً، لذلك يجب تحديد:
channelIdchannelNamechannelDescription
أما على iOS فيمكن ضبط التفاصيل عبر IOSNotificationDetails:
const IOSNotificationDetails iOSPlatformChannelSpecifics = IOSNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
sound: 'default.wav',
badgeNumber: 1,
subtitle: 'تنبيه فرعي',
);
ثم نُنشئ كائن NotificationDetails الذي يجمع هذه التفاصيل:
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
أو:
const NotificationDetails platformChannelSpecifics =
NotificationDetails(iOS: iOSPlatformChannelSpecifics);
وأخيراً نستخدم الدالة show() لإظهار الإشعار:
await flutterLocalNotificationsPlugin.show(
12345,
'إشعار من التطبيق',
'تم إرسال هذا الإشعار باستخدام الحزمة المحلية في Flutter',
platformChannelSpecifics,
payload: 'data',
);
شرح معاملات الدالة show()
id: المعرّف الفريد للإشعار، ويجب أن يكون مختلفاً لكل إشعار إذا أردت التحكم به لاحقاً.title: عنوان الإشعار.body: النص الرئيسي الذي سيظهر للمستخدم.notificationDetails: تفاصيل التنسيق والسلوك الخاصة بالإشعار.payload: بيانات إضافية يمكن استخدامها عند الضغط على الإشعار.
2) كيفية جدولة إشعار محلي في وقت لاحق
إذا كنت تريد إرسال إشعار في تاريخ أو وقت مستقبلي، فمن الأفضل استخدام الجدولة المعتمدة على المنطقة الزمنية timezone. هذا يحل مشكلات اختلاف التوقيت أو التبديل الموسمي.
الحزمة نفسها توفر دعماً لمكتبة timezone، لذا يكفي استيرادها وتهيئتها:
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
Future<void> init() async {
final AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: null,
);
tz.initializeTimeZones();
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onSelectNotification: selectNotification,
);
}
بعد ذلك استخدم الدالة zonedSchedule():
await flutterLocalNotificationsPlugin.zonedSchedule(
12345,
'إشعار من التطبيق',
'هذا الإشعار المجدول تم إنشاؤه باستخدام Flutter Local Notifications',
tz.TZDateTime.now(tz.local).add(const Duration(days: 3)),
const NotificationDetails(
android: AndroidNotificationDetails(
'channel_id',
'channel_name',
'channel_description',
),
),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
);
أهم معاملات الدالة zonedSchedule()
scheduledDate: الوقت الذي سيتم فيه إطلاق الإشعار.uiLocalNotificationDateInterpretation: طريقة تفسير الوقت في بعض إصداراتiOSالقديمة.androidAllowWhileIdle: يسمح بإرسال الإشعار حتى عندما يكون الجهاز في وضع توفير الطاقة.
هذه الطريقة مناسبة لتطبيقات التذكير، وجدولة المهام، والتنبيهات الدورية، والتنبيهات المرتبطة بالتقويم أو المناسبات.
3) كيفية إلغاء إشعار محلي
توفر الحزمة خيارين أساسيين للإلغاء:
- إلغاء إشعار محدد عبر المعرّف
id. - إلغاء جميع الإشعارات المعلقة دفعة واحدة.
لإلغاء إشعار واحد:
await flutterLocalNotificationsPlugin.cancel(NOTIFICATION_ID);
ولإلغاء جميع الإشعارات:
await flutterLocalNotificationsPlugin.cancelAll();
هذه الوظائف مهمة في التطبيقات التي تتغير فيها حالة المهام، مثل حذف تذكير أو إنهاء موعد لم يعد يحتاج إلى تنبيه.
نصائح عملية لتحسين تجربة المستخدم مع الإشعارات
- استخدم عناوين واضحة ومختصرة داخل الإشعار.
- تجنب الإكثار من التنبيهات حتى لا يشعر المستخدم بالإزعاج.
- اربط كل إشعار بإجراء منطقي عند النقر عليه باستخدام
payload. - اختر قنوات إشعار مناسبة في
Androidحتى يتمكن المستخدم من التحكم في الأولوية والصوت. - اختبر سلوك الإشعارات في وضع الواجهة الأمامية والخلفية على
iOSوAndroid.
متى تستخدم الإشعارات المحلية بدلاً من Push Notifications؟
الإشعارات المحلية تكون الخيار الأفضل عندما يكون منطق التنبيه معتمداً على بيانات موجودة داخل الجهاز أو على أحداث يقررها التطبيق نفسه، مثل:
- تذكير يومي بشرب الماء أو ممارسة الرياضة.
- تنبيه بمهمة مجدولة داخل التطبيق.
- تذكير بموعد أو مناسبة محفوظة مسبقاً.
- إشعار بانتهاء مؤقت أو عدّاد زمني.
أما إذا كان الإشعار يعتمد على حدث يحدث في الخادم، مثل رسالة جديدة أو تغيير في حساب المستخدم، فعادة يكون استخدام Push Notifications هو الأنسب.
الخلاصة التقنية
إعداد الإشعارات المحلية في Flutter ليس معقداً، لكنه يتطلب فهماً جيداً للفروقات بين Android وiOS. أفضل نهج عملي هو عزل كل المنطق داخل خدمة مستقلة، وتهيئة المكتبة مبكراً عند تشغيل التطبيق، ثم استخدام الدوال المناسبة مثل show() وzonedSchedule() وcancel(). تقنياً، الإشعارات المحلية تمنحك مرونة ممتازة لبناء تجربة تذكير وتفاعل فعالة دون الاعتماد على خوادم خارجية، ما يجعلها خياراً مثالياً لكثير من تطبيقات الإنتاجية والتعليم والمهام اليومية.