Flutter
تعرض nida_inapp اللافتات والنوافذ المنبثقة ورسائل ملء الشاشة وصندوق الرسائل، وتتولى فتح
الإشعارات وtrack والموافقة. تُرسم كل رسالة بعناصر Flutter، بلا واجهة أصلية ولا WebView، فتتصرّف
الاستدعاءات بالطريقة نفسها على Android وiOS، وتعمل كل رسالة باتجاهها داخل Directionality. ويُبلَّغ
عن مرات الظهور والنقرات والإغلاق، وتُحفظ في طابور على الجهاز وتُعاد. ولا ترمي الحزمة أي خطأ في التطبيق.
التثبيت
لم تُنشر بعد
الحزمة غير منشورة على pub.dev بعد: تصلك مع حسابك، بالاسم أدناه.
dependencies:
nida_inapp: ^0.1.0
الاستعمال
final navigatorKey = GlobalKey<NavigatorState>();
AppMessages.configure(AppMessagesOptions(
appKey: 'ipk_…', // التطبيق ← مفاتيح الرسائل (عام)
apiUrl: 'https://<your in-app host>',
placements: ['home_top', 'home_popup', 'inbox'], // كل موضع يعرضه التطبيق، 10 على الأكثر
profilesUrl: 'https://<your profiles host>', // لـ track والموافقة
profilesKey: 'pk_…',
onOpenUrl: (url, message) => myRouter.open(url), // لا تفتح الحزمة أي رابط بنفسها
onPushOpened: (messageId, placement) => myRouter.openOffers(),
));
await AppMessages.identify(user.id); // معرّفك الخاص؛ لا يظهر شيء قبله
await AppMessages.setConsent('in_app', true); // صارمة: دونها لا يُقرَّر شيء
runApp(AppMessagesScope(navigatorKey: navigatorKey, child: MyApp(navigatorKey: navigatorKey)));
// حيث يريدها التطبيق:
const MessageBanner(placement: 'home_top'); // لا يبني شيئًا حتى تجهز لافتة
const MessageInbox(placement: 'inbox'); // قائمة؛ ضعها في شاشة خاصة بها
const AppMessagesDebugView(); // معرّف التثبيت، لأجهزة الاختبار
قرار واحد في الجلسة
تبدأ الجلسة عند configure، وعند identify لمستخدم آخر، وعند العودة إلى الواجهة بعد
sessionTimeout (30 دقيقة). وفي كل جلسة قرار واحد لكل المواضع المضبوطة، وتعرض العناصر من جوابه،
فلافتة على عشر شاشات تبقى استدعاءً واحدًا.
setConsent('in_app', true)وAppMessages.refresh()تقرّران مرة أخرى عن قصد.- لكل تثبيت 5 قرارات متتالية، ثم قرار كل 3 دقائق. وحين يُطلب منها الانتظار تحتفظ الحزمة بآخر قرار حتى يحق لها القرار من جديد. وكذلك دون اتصال.
- يُحفظ رمز كل قرار على الجهاز لمستخدمه ويُعاد مع القرار التالي، فتعيد الخدمة استعمال اختياراتها. ولا يرسل مستخدم رمز مستخدم آخر أبدًا.
الصور
تُحمَّل صورة الرسالة في ذاكرة صور Flutter قبل أن تُعدّ الرسالة جاهزة، وتبقى هناك ما دام يمكن عرضها. والصورة التي تفشل (دون اتصال، أو 404، أو 10 ثوانٍ بلا جواب) تعني تخطّي تلك الرسالة في هذه الجلسة: لا تظهر مكسورة أبدًا.
لا تحتفظ الحزمة بذاكرة على القرص. ولصور تبقى بعد إعادة التشغيل، مرّر مزوّد صور يخزّن على القرص بصفته
imageProvider.
النوافذ المنبثقة ورسائل ملء الشاشة
تنتظر النافذة الجاهزة (حوار) أو رسالة ملء الشاشة حتى يُركَّب AppMessagesScope ويوجد متصفّح تنقّله
ويكون التطبيق في الواجهة. ولا تُسقط لأنها جاءت مبكرًا. وتظهر واحدة في كل مرة، وحين تُغلق تأتي التالية.
ولا تظهر النافذة التي عُرضت في الجلسة مرة أخرى فيها. والقرار الأحدث يستبدل النافذة المنتظرة لموضعها،
أو يسحبها.
صندوق الرسائل
تُحفظ كل بطاقة يسلّمها قرار للمستخدم على الجهاز، الأحدث أولًا، 30 يومًا، و50 بطاقة على الأكثر.
- فتح بطاقة يعلّمها مقروءة ويبلغ عن نقرة، وحذفها يبلغ عن إغلاق ويزيلها. وحالة القراءة محلية على الجهاز.
- الصندوق لمستخدم واحد: تعريف شخص آخر، أو
reset()، يفرغه. - البطاقة التي لم تُخزَّن صورتها (دون اتصال) تبقى خارج القائمة حتى تُخزَّن.
AppMessages.inbox(من نوعValueListenable) وunreadCountلشارة، أو لصندوق رسائل خاص بك.
أن ترسم الرسائل بنفسك
استعمل AppMessages.decide() (الرسائل الجاهزة، مع القرار أولًا إن لم تقرّر الجلسة)، وAppMessages.messages
(تدفّق منها)، وmessageFor. وأبلغ عمّا حدث بـ reportImpression وreportClick
وreportDismiss(messageId). ومرّر presentPopups: false ليترك النطاق النوافذ المنبثقة لك.
التقارير
- يُبلَّغ عن الظهور حين تُرسم الرسالة فعلًا، وعن النقرة من زر الإجراء (ويذهب الرابط إلى
onOpenUrl)، وعن الإغلاق حين تُغلق. ويُبلَّغ عن كلٍّ منها مرة واحدة لكل رسالة في الجلسة. - لكل حدث معرّفه الخاص منذ وقوعه، فالدفعة المعادة تُحسب مرة واحدة.
- تُحفظ الأحداث على الجهاز (500 على الأكثر، 7 أيام) وتُرسل دفعات من 50. ودون اتصال، أو حين يُطلب الانتظار، تبقى وتُرسل لاحقًا.
التتبّع والموافقة
AppMessages.track('cart_viewed', {'items': 2}) وAppMessages.setConsent('in_app', true)
يذهبان إلى ملف المستخدم بمفتاح profilesKey. الموافقة صارمة: المستخدم الذي لم يمنح in_app لا
يحصل على شيء من القرار، حتى لرسالة موجّهة للجميع.
الإشعارات
لا تسجّل الحزمة أي معالج إشعارات، فلا تتنازع عليه مع firebase_messaging أو
flutter_local_notifications، ولا تملك قناة إشعارات ولا تعرض أي إشعار.
- يسأل خادمك أي إشعار يرسل (بمفتاح سري
isk_…) وينسخpush_dataمن الجواب فيdataرسالة FCM كما هي. - حين يفتح المستخدم الإشعار، مرّر بياناته إلى
AppMessages.handlePush(data)، منFirebaseMessaging.onMessageOpenedAppأوgetInitialMessage()أو معالج الضغط فيflutter_local_notifications. - لإشعار نداء تبلغ الحزمة عن النقرة، وتستدعي
onPushOpened(messageId, placement)وتعيد true. وأي إشعار آخر تعيد له false وتتركه.
والإشعار المسلَّم قبل configure (تشغيل من الإشعار) ينتظره. ودون مستخدم معرَّف يصلك الاستدعاء، لكن
لا يُبلَّغ عن نقرة.
أجهزة الاختبار
يعرض AppMessagesDebugView معرّف التثبيت. أضف معرّف مستخدم المختبر ومعرّف التثبيت ذاك في أجهزة
الاختبار. فيحصل جهاز الاختبار على المسودات المرسلة إلى أجهزة الاختبار، كل منها بشارة «اختبار» صغيرة
(AppMessage.test). ويمنح المختبر موافقة in_app كأي أحد.
اللغة
تطلب القرارات en أو ar (لغة الجهاز افتراضيًا). والتطبيق الذي له مبدّل لغة خاص يستدعي
AppMessages.setLanguage('ar')، فيقرّر مرة أخرى بتلك اللغة. وتعمل نصوص الرسالة باتجاهها، وكلمات
الحزمة القليلة، مثل إغلاق واختبار، تتبع لغة التطبيق.