agentsclimarketplace

Cometchat flutter v5 push

Skill cometchat/cometchat-skills/skills/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.From its SKILL.md

Install
npx -y skills add cometchat/cometchat-skills --skill cometchat-flutter-v5-push

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

10.4 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it

Ground truth: cometchat_chat_uikit: ^5.2 (legacy/maintenance-only; calls via raw cometchat_calls_sdk ^5.0.2) — pub-cache source + ui-kit/flutter/v5. Official docs: https://www.cometchat.com/docs/notifications/overview · Docs MCP: claude mcp add --transport http cometchat-docs https://www.cometchat.com/docs/mcp (or fetch the URL directly without MCP). Verify symbols against the installed package/source before relying on them.

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 directly — PNRegistry 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

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.