Skip to main content
The Flutter UI Kit push-notification demo already solves permission prompts, Firebase/APNs registration, PushKit, and CallKit synchronization. This guide distills that reference implementation so you can bring the exact experience into any Flutter app that uses CometChat UI Kit and Calls UI Kit.

Reference implementation

Browse the full push-notification sample (Flutter + native iOS glue) to diff or copy files.
The steps below refer to the folder names inside the sample repo (for example lib/notifications or ios/Runner/AppDelegate.swift). Copy the same structure into your project and only change identifiers (bundle ID, provider IDs, package name) to match your app.

Architecture map

Sample pathRole inside the appWhat to copy/replicate
lib/notificationsModels, platform services, push registry helpers, MethodChannel bridge handlersCopy the directory as-is when starting a new app; keep the folder boundaries so imports resolve.
lib/main.dartInitializes Firebase, SharedPreferences, local notifications, and wires APNSService.setupNativeCallListenerReuse the launch sequence so notification taps from a terminated state reach Flutter before runApp.
lib/dashboard.dart / lib/guard_screen.dartBoots CometChat UI Kit, requests permissions, registers Call/Notification listenersMirror the lifecycle hooks to initialize APNSService only once and capture pending notification taps.
ios/Runner/AppDelegate.swiftPushKit + CallKit bridge plus MethodChannel glue for VoIP token + call eventsStart from the sample file and update the bundle identifiers or localized strings.
lib/notifications/services/pn_registry.dartWraps CometChatNotifications.registerPushToken for APNs, VoIP, and FCMSupply your CometChat provider IDs so registration works in every environment.

1. Prerequisites

  • Apple Developer account with Push Notifications, Background Modes, and VoIP entitlements for your bundle ID.
  • Firebase project with an iOS app configured (GoogleService-Info.plist inside the Runner target) and Cloud Messaging enabled.
  • CometChat app credentials (App ID, Region, Auth Key) plus Push Notification extension enabled with APNs + VoIP providers created.
  • Flutter 3.24+ / Dart 3+, the latest CometChat UI Kit (cometchat_chat_uikit) and Calls UI Kit (cometchat_calls_uikit) packages.
  • Physical iPhone or iPad for testing—simulators cannot receive VoIP pushes or present CallKit UI.

2. Prepare credentials before coding

2.1 Apple Developer portal

  1. Generate an APNs Auth Key (.p8) and note the Key ID and Team ID.
  2. Enable Push Notifications plus Background Modes → Remote notifications and Voice over IP on the bundle ID.
  3. Create a VoIP Services certificate/key if you want separate credentials.

2.2 Firebase Console

  1. Register the same bundle ID and download GoogleService-Info.plist.
  2. Enable Cloud Messaging and, if needed, APNs authentication key under Project Settings → Cloud Messaging.

2.3 CometChat dashboard

  1. Turn on the Push Notifications extension (V2).
CometChat Dashboard - Push Notifications
  1. Create one provider for APNs device pushes and (optionally) another for VoIP pushes.
CometChat Dashboard - Push Notifications
  1. Copy the generated provider IDs—they are required inside CometChatConfig.

2.4 Local configuration files

Update lib/cometchat_config.dart (or your own config file) so it exposes:
class CometChatConfig {
  static const appId = "YOUR_APP_ID";
  static const region = "YOUR_APP_REGION";
  static const authKey = "YOUR_AUTH_KEY";
  static const fcmProviderId = "FCM-PROVIDER-ID";
  static const apnProviderId = "APNS-PROVIDER-ID";
  static const apnVoipProviderId = "APNS-VOIP-PROVIDER-ID"; // optional but recommended
}

3. Bring the notification stack into Flutter

3.1 Copy lib/notifications

  • Clone or download the sample once.
  • Copy the entire lib/notifications directory (models, Android/iOS services, helpers) into your app.
  • Update the import prefixes (for example replace package:flutter_application_demo/... with your own package name). Keeping the same folder names avoids manual refactors later.

3.2 Wire the entry points

lib/main.dart
  • Initialize SharedPreferencesClass, Firebase, and FlutterLocalNotificationsPlugin before calling runApp.
  • Store NotificationLaunchHandler.pendingNotificationResponse when the app is opened by tapping a notification while terminated.
  • On iOS, call APNSService.setupNativeCallListener(context) from initState so Flutter reacts when the native CallKit UI changes state.
lib/guard_screen.dart / lib/dashboard.dart (or your first screen after login)
  • Ensure CometChatUIKit.init() and CometChatUIKit.login() finish before rendering the dashboard.
  • Instantiate APNSService (iOS only) and call apnsService.init(context) inside initState.
  • Register CometChat UI + Calls listeners (CometChatUIEventListener, CometChatCallEventListener, and CallListener) exactly once per session; the sample stores the listener IDs inside APNSService.
  • Replay NotificationLaunchHandler.pendingNotificationResponse after the widget tree builds so taps from a killed app still navigate to MessagesScreen.
  • Forward lifecycle changes to IncomingCallOverlay / BoolSingleton to hide stale overlays when the app resumes.

3.3 Align dependencies and configuration

Mirror the sample pubspec.yaml versions (update as needed when newer releases ship):
dependencies:
  firebase_core: ^3.0.0
  firebase_messaging: ^15.0.0
  flutter_apns_x: ^2.1.1
  flutter_callkit_incoming: ^2.0.3+3
  flutter_local_notifications: ^16.0.0
  cometchat_chat_uikit: ^5.0.0
  cometchat_calls_uikit: ^5.0.0
  permission_handler: ^11.3.0
Run flutter pub get, then flutterfire configure if you still need to generate firebase_options.dart.

4. Configure the native iOS layer

4.1 Capabilities and Info.plist

  1. Open ios/Runner.xcworkspace in Xcode.
  2. Under Signing & Capabilities, enable Push Notifications and Background Modes (Remote notifications + Voice over IP).
  3. Add microphone, camera, Bluetooth, and notification permission strings to Info.plist.
  4. Set the development team that has access to the APNs/VoIP keys you generated earlier.

4.2 AppDelegate.swift bridge

Start from the sample ios/Runner/AppDelegate.swift and keep these pieces intact:
  • MethodChannel handshake – create a channel that both Flutter and Swift know:
let appInfoChannel = FlutterMethodChannel(
  name: "com.flutter_application_demo/ios",
  binaryMessenger: controller.binaryMessenger
)
Handle at least getAppInfo, endCall, onCallAcceptedFromNative, and onCallEndedFromNative, mirroring the Dart side (APNSService.setupNativeCallListener).
  • Firebase + plugin registration – call FirebaseApp.configure() before GeneratedPluginRegistrant.register(with: self).
  • PushKit – instantiate PKPushRegistry, set desiredPushTypes = [.voIP], and forward the token inside pushRegistry(_:didUpdate:for:) via SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(tokenHex).
  • CallKit – configure CXProviderConfiguration, keep a CXCallController, and implement provider(_:perform: CXAnswerCallAction) / provider(_:perform: CXEndCallAction) so native actions propagate to Flutter.
  • Incoming push handler – inside pushRegistry(_:didReceiveIncomingPushWith:), convert the CometChat payload into flutter_callkit_incoming.Data, set extra with the raw payload, and call showCallkitIncoming(..., fromPushKit: true).
  • UUID helper – reuse createUUID(sessionid:) to produce valid UUIDs from long CometChat session IDs; this lets CallKit correlate calls even if the payload string exceeds 32 characters.
If you change the MethodChannel name in Swift, remember to update APNSService.platform in Dart to match.

5. Token registration and runtime events

5.1 Standard APNs tokens

APNSService hooks into FirebaseMessaging.instance.getAPNSToken() (and onTokenRefresh) before calling:
await CometChatPushRegistry.register(
  token: token,
  isFcm: false,
  isVoip: false,
);
This registers the device token against the APNs provider selected in CometChatConfig.apnProviderId.

5.2 VoIP tokens

  • Capture the PushKit token in AppDelegate.pushRegistry(_:didUpdate:for:).
  • Forward it to Flutter via the MethodChannel or register it directly from Swift by invoking CometChatPushRegistry through SwiftFlutterCallkitIncomingPlugin.
  • If you keep the Dart implementation, emit a MethodChannel call named onVoipToken and handle it in APNSService by calling CometChatPushRegistry.register(token: token, isFcm: false, isVoip: true);.

5.3 Local notifications and navigation

  • APNSService._showNotification displays a local notification when the incoming CometChat message does not belong to the currently open conversation.
  • LocalNotificationService.handleNotificationTap parses the payload, fetches the relevant user/group, and pushes MessagesScreen.
  • NotificationLaunchHandler.pendingNotificationResponse caches taps triggered while the app is terminated; replay it on the dashboard once the UI is ready.

5.4 Call events

  • FlutterCallkitIncoming.onEvent is already wired inside APNSService to accept or end calls initiated by CallKit.
  • When native CallKit UI accepts/ends a call, Swift invokes onCallAcceptedFromNative / onCallEndedFromNative on the MethodChannel; APNSService then calls FlutterCallkitIncoming.setCallConnected or CometChat.endCall() to keep both stacks synchronized.

6. Testing checklist

  1. Run the app on a physical device in debug first. Grant notification, microphone, camera, and Bluetooth permissions when prompted.
  2. From the CometChat Dashboard, send a standard message notification. Verify:
    • Foreground: a local notification banner shows (unless you are in that chat).
    • Background: APNs notification appears, tapping opens the right conversation.
  3. Force-quit the app, send another message push, tap it, and confirm NotificationLaunchHandler launches MessagesScreen.
  4. Trigger an incoming CometChat call. Ensure:
    • CallKit UI shows contact name, call type, and Accept/Decline.
    • Accepting on the lock screen notifies Flutter (setupNativeCallListener), starts the call session, and dismisses the native UI when the call ends.
  5. Decline the call and confirm both CallKit and Flutter clean up (BoolSingleton resets, overlays dismissed).
  6. Rotate through Wi-Fi/cellular and reinstall the app to confirm token registration works after refresh events.

7. Troubleshooting tips

SymptomQuick checks
No VoIP pushesEntitlements missing? Ensure Push Notifications + Background Modes (VoIP) are enabled and the bundle ID matches the CometChat provider.
CallKit UI never dismissesMake sure endCall(callUUID:) reports to CXProvider, runs a CXEndCallAction, and calls SwiftFlutterCallkitIncomingPlugin.sharedInstance?.endCall.
Flutter never receives native call eventsConfirm the MethodChannel names match between Swift and Dart, and APNSService.setupNativeCallListener runs inside initState.
Token registration errorsDouble-check CometChatConfig provider IDs, and verify you call registerPushToken after CometChatUIKit.login succeeds.
Notification taps ignoredEnsure you replay NotificationLaunchHandler.pendingNotificationResponse after the navigator key is ready (typically WidgetsBinding.instance.addPostFrameCallback).

Additional resources