Skillquality 0.46

cometchat-flutter-v5-push

Use when implementing push notifications with CometChat Flutter UIKit v5. Covers FCM (Android), APNs (iOS), VoIP calls, token lifecycle, local notifications, and tap-to-navigate.

Price
free
Protocol
skill
Verified
no

What it does

CometChat Flutter UIKit v5 — Push Notifications

Push notification setup for Android (FCM) and iOS (APNs + VoIP).

Dependencies

dependencies:
  firebase_core: ^3.9.0
  firebase_messaging: ^15.1.6
  flutter_local_notifications: ^18.0.0
  flutter_callkit_incoming: # for VoIP call notifications
  app_badge_plus: ^1.2.6  # badge count

Architecture Overview

notifications/
├── models/
│   ├── payload.dart              # PayloadData model for parsing FCM data
│   ├── call_action.dart          # CallAction enum (initiated, cancelled, unanswered)
│   ├── call_type.dart            # CallType enum (audio, video)
│   └── notification_message_type.dart  # Message type constants
├── services/
│   ├── android_notification_service/
│   │   ├── firebase_services.dart       # FCM init, listeners, token management
│   │   ├── local_notification_handler.dart  # Local notification display + tap handling
│   │   ├── voip_notification_handler.dart   # VoIP call display, accept, decline
│   │   └── notification_launch_handler.dart # Terminated state launch handling
│   ├── iOS_notification_service/
│   │   └── apns_services.dart           # APNs connector, VoIP token, CallKit
│   └── cometchat_service/
│       └── cometchat_services.dart      # PNRegistry (token registration/unregistration)

Token Registration — CometChatNotifications.registerPushToken

The kit's only public push surface is CometChatNotifications.registerPushToken(platform, {providerId, fcmToken, deviceToken, voipToken, onSuccess, onError}) (and unregisterPushToken({onSuccess, onError})). The sample app wraps this in an extension named PNRegistry on CometChatService (see sample_app_push_notifications/lib/notifications/services/cometchat_service/cometchat_services.dart) that picks the right provider ID + platform constant for FCM-Android / FCM-iOS / APNs / APNs-VoIP. Copy that helper into your project, or call CometChatNotifications.registerPushToken directlyPNRegistry is a sample-app extension, not importable from any cometchat package.

// Direct kit API:
import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';

await CometChatNotifications.registerPushToken(
  PushPlatforms.FCM_FLUTTER_ANDROID,    // platform — first positional arg
  providerId: fcmProviderId,            // dashboard FCM provider ID
  fcmToken: token,                      // use fcmToken / deviceToken / voipToken depending on platform
  onSuccess: (response) => debugPrint('registered: $response'),
  onError: (e) => debugPrint('register failed: $e'),
);

// On logout:
await CometChatNotifications.unregisterPushToken(
  onSuccess: (_) {},
  onError: (e) => debugPrint('unregister failed: $e'),
);
// Or use the sample-app PNRegistry helper after copying it into your project:
PNRegistry.registerPNService(token, true, false);   // (token, isFcm, isVoip)
PNRegistry.unregisterPNService();

Platform mapping:

  • FCM Android → PushPlatforms.FCM_FLUTTER_ANDROID
  • FCM iOS → PushPlatforms.FCM_FLUTTER_IOS
  • APNs Device → PushPlatforms.APNS_FLUTTER_DEVICE
  • APNs VoIP → PushPlatforms.APNS_FLUTTER_VOIP

Provider IDs come from AppCredentials.fcmProviderId / AppCredentials.apnProviderId (your own constants — these are dashboard-configured values, not kit exports).

The remaining examples below assume you've copied PNRegistry from the sample app. If you call CometChatNotifications.registerPushToken directly, swap the call sites accordingly.

Android — FCM Setup

1. Background handler (must be top-level function)

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage rMessage) async {
  LocalNotificationService.showNotification(rMessage.data, rMessage, "", false);
  await VoipNotificationHandler.displayIncomingCall(rMessage);
}

2. Initialize in dashboard/home screen

class FirebaseService {
  Future<void> init(BuildContext context) async {
    _firebaseMessaging = FirebaseMessaging.instance;
    await requestPermissions();
    await initListeners(context);

    String? token = await _firebaseMessaging.getToken();
    if (token != null) {
      PNRegistry.registerPNService(token, true, false);
    }
  }
}

3. Listener setup

// Background messages
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);

// Token refresh
_firebaseMessaging.onTokenRefresh.listen((token) {
  PNRegistry.registerPNService(token, true, false);
});

// Foreground messages
FirebaseMessaging.onMessage.listen((message) {
  LocalNotificationService.showNotification(message.data, message, conversationId, isAgentic);
});

// Tap from background
FirebaseMessaging.onMessageOpenedApp.listen((message) {
  openNotification(context, message, conversationId);
});

// Tap from terminated state
FirebaseMessaging.instance.getInitialMessage().then((message) {
  if (message != null) openNotification(context, message, conversationId);
});

iOS — APNs Setup

final _connector = ApnsPushConnector();
_connector.shouldPresent = (x) => Future.value(false);

_connector.configure(
  onLaunch: (message) async { openNotification(message, context, ""); },
  onResume: (message) async { openNotification(message, context, conversationId); },
  onMessage: (message) async { _showNotification(message.data, message, conversationId, isAgentic); },
);

_connector.requestNotificationPermissions();

// APNs device token
_connector.token.addListener(() {
  PNRegistry.registerPNService(_connector.token.value!, false, false);
});

// VoIP token
FlutterCallkitIncoming.getDevicePushTokenVoIP().then((voipToken) {
  PNRegistry.registerPNService(voipToken, false, true);
});

VoIP Call Notifications

Display incoming call (both platforms)

static Future<void> displayIncomingCall(RemoteMessage rMessage) async {
  PayloadData callPayload = PayloadData.fromJson(rMessage.data);
  if (callPayload.type == 'call' && callPayload.callAction == CallAction.initiated) {
    CallKitParams params = CallKitParams(
      id: callPayload.sessionId,
      nameCaller: callPayload.senderName,
      type: (callPayload.callType == CallType.audio) ? 0 : 1,
      duration: 45000,
    );
    await FlutterCallkitIncoming.showCallkitIncoming(params);
  }
}

Accept/Decline via CallKit events

FlutterCallkitIncoming.onEvent.listen((CallEvent? callEvent) {
  switch (callEvent?.event) {
    case Event.actionCallAccept:
      VoipNotificationHandler.acceptVoipCall(callEvent, context);
      break;
    case Event.actionCallDecline:
      VoipNotificationHandler.declineVoipCall(callEvent);
      break;
    case Event.actionCallTimeout:
    case Event.actionCallEnded:
      VoipNotificationHandler.endCall(sessionId: callEvent?.body['id']);
      break;
  }
});

Local Notification Display

Uses flutter_local_notifications with inbox-style grouping per conversation:

// Skip if user is viewing the same conversation
if (conversationId == notifConversationId) return;

// Skip call-type notifications (handled by CallKit)
if (data["type"] == "call") return;

// Show with stable ID per conversation (replaces previous)
final notificationId = conversationId.hashCode;
await flutterLocalNotificationsPlugin.show(notificationId, title, body, details, payload: jsonPayload);

Tap-to-Navigate

static void handleNotificationTap(NotificationResponse? response) async {
  if (response?.payload != null) {
    final body = jsonDecode(response!.payload!);
    NotificationDataModel model = NotificationDataModel.fromJson(body);

    User? user; Group? group;
    if (model.receiverType == "user") {
      user = await CometChat.getUser(model.sender);
    } else {
      group = await CometChat.getGroup(model.receiver);
    }

    if (model.type == "chat" && (user != null || group != null)) {
      Navigator.of(CallNavigationContext.navigatorKey.currentContext!).push(
        MaterialPageRoute(builder: (_) => MessagesSample(user: user, group: group)),
      );
    }
  }
}

Terminated State Handling

// In main()
final launchDetails = await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
if (launchDetails?.didNotificationLaunchApp == true) {
  NotificationLaunchHandler.pendingNotificationResponse = launchDetails!.notificationResponse;
}

// In dashboard initState()
Future.delayed(Duration(milliseconds: 300), () {
  final response = NotificationLaunchHandler.pendingNotificationResponse;
  if (response != null) {
    NotificationLaunchHandler.pendingNotificationResponse = null;
    LocalNotificationService.handleNotificationTap(response, isTerminatedState: true);
  }
});

Logout — Unregister Token

PNRegistry.unregisterPNService();
// Then: CometChatUIKit.logout(...)

Checklist — Push Notifications

  • Firebase initialized before CometChat init
  • FCM token registered via PNRegistry.registerPNService(token, true, false)
  • APNs device + VoIP tokens registered on iOS
  • Background handler is top-level @pragma('vm:entry-point') function
  • Token refresh listener re-registers token
  • Local notifications skip current active conversation
  • Call notifications handled via FlutterCallkitIncoming, not local notifications
  • Tap-to-navigate uses CallNavigationContext.navigatorKey.currentContext
  • Tokens unregistered on logout via PNRegistry.unregisterPNService()
  • Terminated state launch handled via NotificationLaunchHandler

Capabilities

skillsource-cometchatskill-cometchat-flutter-v5-pushtopic-agent-skillstopic-ai-agenttopic-chattopic-claude-codetopic-cometchattopic-cursortopic-messagingtopic-nextjstopic-reacttopic-react-nativetopic-ui-kit

Install

Quality

0.46/ 1.00

deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (9,528 chars)

Provenance

Indexed fromgithub
Enriched2026-05-18 19:04:50Z · deterministic:skill-github:v1 · v1
First seen2026-05-07
Last seen2026-05-18

Agent access