notification_voip_plugin 2.0.1 copy "notification_voip_plugin: ^2.0.1" to clipboard
notification_voip_plugin: ^2.0.1 copied to clipboard

A Flutter plugin for notifications (8 templates, grouping) and VoIP calls (CallKit/Telecom, custom Flutter screen) across 6 platforms.

notification_voip_plugin #

A Flutter plugin for notifications and VoIP calls across Android, iOS, Web, macOS, Windows, and Linux.

pub package license


What It Does #

This plugin gives you a single Dart API to handle:

  • System notifications with 8 visual templates (big text, big picture, progress bar, action buttons, and more)
  • In-app banner overlays that slide in from the top
  • VoIP calls with native call UI (CallKit on iOS, ConnectionService on Android) or a custom Flutter call screen
  • Push tokens (FCM, APNs, VoIP via PushKit)
  • Badge count management
  • Live Activities on iOS (Dynamic Island / Lock Screen) — experimental

Everything is accessed through NotificationVoipPlugin.* — no instances, no singletons, no boilerplate.


Platform Support #

Feature Android iOS macOS Windows Linux Web
System Notifications stub stub
In-App Banners
Notification Templates
VoIP Calls (Native UI)
Custom Call Screen
Badge Count
FCM Token
APNs Token
VoIP Token
Live Activities ⚠️ ✅ (16.2+)
Phone Account Check

Quick Start #

1. Install #

dependencies:
  notification_voip_plugin: ^2.0.0
flutter pub get

2. Initialize #

import 'package:notification_voip_plugin/notification_voip_plugin.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await NotificationVoipPlugin.init(
    const NvpConfig(
      appName: 'My App',
      channelId: 'my_channel',
      channelName: 'My Channel',
    ),
  );
  runApp(const MyApp());
}

NvpConfig is optional — init() works with sensible defaults.

3. Request Permission #

final granted = await NotificationVoipPlugin.requestPermission();

4. Show a Notification #

await NotificationVoipPlugin.showNotification(
  const NvpNotification(title: 'Hello', body: 'World'),
);

That's it. You're up and running.


Setup #

Android #

Minimum SDK — set minSdk 24 in android/app/build.gradle:

android {
    defaultConfig {
        minSdk = 24
    }
}

Firebase (for FCM tokens) — add Google Services plugin to android/build.gradle:

dependencies {
    classpath 'com.google.gms:google-services:4.4.0'
}

Apply it in android/app/build.gradle:

apply plugin: 'com.google.gms.google-services'

Place your google-services.json in android/app/.

Permissions — the plugin's manifest already declares all required permissions (merged automatically):

POST_NOTIFICATIONS, MANAGE_OWN_CALLS, FOREGROUND_SERVICE,
RECORD_AUDIO, BLUETOOTH, BLUETOOTH_ADMIN, READ_PHONE_STATE,
READ_PHONE_NUMBERS, ANSWER_PHONE_CALLS

No additional manifest entries needed.

Phone Account (VoIP) — on Android, the user must enable the VoIP phone account in system settings before incoming calls work:

final enabled = await NotificationVoipPlugin.isPhoneAccountEnabled();
if (!enabled) {
  await NotificationVoipPlugin.openPhoneAccountSettings();
}

iOS #

Deployment Target — iOS 14.0+ in your ios/Podfile:

platform :ios, '14.0'

Xcode Capabilities — enable on your Runner target:

  • Push Notifications
  • Background Modes → check "Voice over IP" and "Remote notifications"

Info.plist — add:

<key>UIBackgroundModes</key>
<array>
    <string>voip</string>
    <string>remote-notification</string>
</array>

APNs Token Forwarding — in AppDelegate.swift:

import notification_voip_plugin

override func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
    NotificationVoipPlugin.shared?.setAPNsToken(deviceToken)
}

Usage #

Notifications #

Simple notification:

await NotificationVoipPlugin.showNotification(
  const NvpNotification(title: 'Hello', body: 'World'),
);

With a template:

// Big text (expandable)
await NotificationVoipPlugin.showNotification(
  const NvpNotification(title: 'Article', body: 'Preview...'),
  template: const NvpNotificationTemplate(
    type: NvpNotificationTemplateType.bigText,
    expandedText: 'Full article text that expands when pulled down.',
  ),
);

// Big picture
await NotificationVoipPlugin.showNotification(
  const NvpNotification(title: 'Photo', body: 'New photo shared'),
  template: const NvpNotificationTemplate(
    type: NvpNotificationTemplateType.bigPicture,
    imageUrl: 'https://example.com/photo.jpg',
  ),
);

// Progress bar
await NotificationVoipPlugin.showNotification(
  const NvpNotification(title: 'Download', body: 'Downloading...', tag: 'dl-1'),
  template: const NvpNotificationTemplate(
    type: NvpNotificationTemplateType.progress,
    progressValue: 65,
    progressMax: 100,
  ),
);

Interactive (action buttons + text input):

await NotificationVoipPlugin.showNotification(
  const NvpNotification(title: 'New Message', body: 'Hey!'),
  template: const NvpNotificationTemplate(
    type: NvpNotificationTemplateType.interactive,
    actions: [
      NvpNotificationAction(id: 'reply', title: 'Reply', isTextInput: true, textInputPlaceholder: 'Type a reply...'),
      NvpNotificationAction(id: 'mark_read', title: 'Mark Read'),
    ],
  ),
);

Grouped notifications:

await NotificationVoipPlugin.showNotification(
  const NvpNotification(title: 'Chat', body: 'New message', groupKey: 'chat-group'),
);

In-app banner overlay:

await NotificationVoipPlugin.showInAppNotification(
  const NvpNotification(title: 'Alert', body: 'This is a banner overlay'),
);

Available template types: normal, richText, bigText, bigPicture, bigBanner, progress, interactive, custom

Badge Count #

await NotificationVoipPlugin.setBadgeCount(5);
final count = await NotificationVoipPlugin.getBadgeCount();
await NotificationVoipPlugin.setBadgeCount(0); // clear

Push Tokens #

final pushToken = await NotificationVoipPlugin.getPushToken();   // FCM on Android, APNs on iOS
final fcmToken  = await NotificationVoipPlugin.getFCMToken();    // Android only
final apnsToken = await NotificationVoipPlugin.getAPNsToken();   // iOS only
final voipToken = await NotificationVoipPlugin.getVoIPToken();   // iOS only

VoIP Calls #

Incoming call (native UI):

try {
  await NotificationVoipPlugin.showIncomingCall(
    const NvpCallConfig(
      callId: 'session-123',
      callerName: 'John Doe',
      isVideo: true,
    ),
  );
} on PlatformException catch (e) {
  if (e.code == 'PHONE_ACCOUNT_NOT_ENABLED') {
    await NotificationVoipPlugin.openPhoneAccountSettings();
  }
}

Incoming call (custom Flutter screen):

await NotificationVoipPlugin.showIncomingCall(
  const NvpCallConfig(
    callId: 'session-123',
    callerName: 'John Doe',
    isVideo: false,
    useNativeScreen: false,
  ),
);

Outgoing call:

await NotificationVoipPlugin.showOutgoingCall(
  const NvpCallConfig(
    callId: 'session-456',
    callerName: 'Jane Doe',
    isOutgoing: true,
  ),
);

Call controls:

await NotificationVoipPlugin.toggleMute('session-123');
await NotificationVoipPlugin.toggleSpeaker('session-123');
await NotificationVoipPlugin.toggleCamera('session-123');
await NotificationVoipPlugin.endCall('session-123');
await NotificationVoipPlugin.endAllCalls(); // cleanup on logout

Deferred connection (e.g., after WebRTC setup):

await NotificationVoipPlugin.setCallConnected('session-123');

Custom Call Screen Widget #

The plugin includes a ready-to-use NvpCallScreen widget:

Navigator.push(context, MaterialPageRoute(
  builder: (_) => NvpCallScreen(
    event: NvpCallEvent(action: NvpCallAction.incoming, callId: 'session-123', callerName: 'John'),
    controller: NvpCallScreenController(callId: 'session-123'),
  ),
));

NvpCallScreenController methods:

Method Returns Description
accept() Future<void> Accept incoming call
reject() Future<void> Reject incoming call
end() Future<void> End current call
toggleMute() Future<bool> Toggle mute, returns new state
toggleSpeaker() Future<bool> Toggle speaker, returns new state
toggleCamera() Future<bool> Toggle camera, returns new state

State getters: isMuted, isSpeakerOn, isCameraOn

If a platform call fails, the controller automatically rolls back the local state.

Event Streams #

// Notification tapped (system or in-app banner)
NotificationVoipPlugin.onNotificationTap.listen((NvpNotification n) {
  print('Tapped: ${n.title}');
});

// VoIP call events (accept, decline, ended, incoming, timeoutEnded)
NotificationVoipPlugin.onCallEvent.listen((NvpCallEvent e) {
  print('Call: ${e.action} (${e.callId})');
});

// Real-time call state changes (mute, speaker, status)
NotificationVoipPlugin.onCallStateChanged.listen((NvpCallState s) {
  print('State: ${s.status.name} muted=${s.isMuted}');
});

// Push token refreshed
NotificationVoipPlugin.onTokenRefresh.listen((String token) {
  print('New token: $token');
});

// Push received in foreground (iOS) — inspect and optionally suppress
NotificationVoipPlugin.onPushReceived.listen((Map<String, dynamic> payload) {
  if (payload['conversationId'] == activeConversationId) {
    NotificationVoipPlugin.suppressNextForegroundNotification();
  }
});

Callbacks via NvpConfig #

As an alternative to streams, register callbacks directly in NvpConfig:

await NotificationVoipPlugin.init(
  NvpConfig(
    appName: 'My App',
    onBannerTap: (notification) => print('Banner: ${notification.title}'),
    onSystemNotificationTap: (notification) => print('Tapped: ${notification.title}'),
    onCallAnswered: (event) => print('Answered: ${event.callId}'),
    onCallDeclined: (event) => print('Declined: ${event.callId}'),
    onCallEnded: (event) => print('Ended: ${event.callId}'),
    onCallIncoming: (event) => print('Incoming: ${event.callId}'),
    onCallTimeoutEnded: (event) => print('Timeout: ${event.callId}'),
    onCallStateChanged: (state) => print('State: ${state.status.name}'),
    onTokenRefresh: (token) => print('Token: $token'),
    onPushReceived: (payload) => print('Push: $payload'),
  ),
);

Live Activities (iOS 16.1+) — ⚠️ Experimental #

This feature is experimental. The API may change in future releases.

Setup:

  1. In Xcode: File → New → Target → Widget Extension
  2. Add NSSupportsLiveActivities = YES to Runner/Info.plist
  3. Copy the example widget from ios/WidgetExtensionExample/NvpLiveActivityWidget.swift
  4. Set Widget Extension deployment target to iOS 16.1+

Usage:

// Check support
final enabled = await NotificationVoipPlugin.areLiveActivitiesEnabled();

// Start
await NotificationVoipPlugin.startLiveActivity(
  const NvpLiveActivity(tag: 'download-1', title: 'Downloading...', progress: 0.0),
);

// Update
await NotificationVoipPlugin.updateLiveActivity(
  const NvpLiveActivity(tag: 'download-1', title: 'Downloading...', body: '25/50 MB', progress: 0.5),
);

// End
await NotificationVoipPlugin.endLiveActivity('download-1');
await NotificationVoipPlugin.endAllLiveActivities();

Cleanup #

await NotificationVoipPlugin.dispose();

This cancels all stream subscriptions, clears cached streams, and releases native resources. The plugin can be re-initialized after dispose.


API Reference #

Initialization & Lifecycle #

Method Description
init([NvpConfig? config]) Initialize the plugin. Call once at app start.
dispose() Clean up all resources and cancel subscriptions.
config Current plugin configuration (getter).

Permissions #

Method Description
requestPermission()Future<bool> Request notification permission. Returns true if granted.
isPermissionGranted()Future<bool> Check if notifications are enabled.
openSettings() Open OS notification settings for this app.

Tokens #

Method Description
getPushToken()Future<String?> Platform push token (FCM on Android, APNs on iOS).
getFCMToken()Future<String?> Firebase Cloud Messaging token (Android).
getAPNsToken()Future<String?> APNs device token (iOS).
getVoIPToken()Future<String?> VoIP push token via PushKit (iOS).

Notifications #

Method Description
showNotification(NvpNotification, {NvpNotificationTemplate?})Future<bool> Show a system notification.
showInAppNotification(NvpNotification, {NvpNotificationTemplate?})Future<bool> Show an in-app banner overlay.
cancelNotification(String tag) Cancel a specific notification by tag.
clearAll() Clear all notifications and dismiss banners.
suppressNextForegroundNotification() Suppress the next foreground notification (iOS).

Badge #

Method Description
setBadgeCount(int count) Set app badge count. Pass 0 to clear.
getBadgeCount()Future<int> Get current badge count.

VoIP / Calls #

Method Description
showIncomingCall(NvpCallConfig) Show incoming call UI. Throws PHONE_ACCOUNT_NOT_ENABLED on Android if needed.
showOutgoingCall(NvpCallConfig) Show outgoing call UI.
endCall(String callId) End a call by session ID.
endAllCalls() End all active calls.
setCallConnected(String callId) Mark a call as connected (for deferred connection flows).
getActiveCallIds()Future<List<String>> Get list of active call IDs.
toggleMute(String callId) Toggle mute.
toggleSpeaker(String callId) Toggle speaker.
toggleCamera(String callId) Toggle camera.

Android-Only #

Method Description
isPhoneAccountEnabled()Future<bool> Check if VoIP phone account is enabled.
openPhoneAccountSettings() Open phone account settings.

Live Activities (iOS) — ⚠️ Experimental #

Method Description
areLiveActivitiesEnabled()Future<bool> Check if Live Activities are supported.
startLiveActivity(NvpLiveActivity)Future<Map?> Start a Live Activity.
updateLiveActivity(NvpLiveActivity)Future<bool> Update by tag.
endLiveActivity(String tag)Future<bool> End by tag.
endAllLiveActivities()Future<bool> End all.

Event Streams #

Stream Type Description
onNotificationTap Stream<NvpNotification> User tapped a notification.
onCallEvent Stream<NvpCallEvent> Call lifecycle events (accept, decline, ended, incoming, timeoutEnded).
onCallStateChanged Stream<NvpCallState> Real-time call state changes.
onTokenRefresh Stream<String> Push token refreshed.
onPushReceived Stream<Map<String, dynamic>> Foreground push received (iOS).

Models #

NvpConfig #

Plugin configuration passed to init().

Field Type Default Description
appName String? null App name for CallKit / ConnectionService display.
bannerDuration Duration 5 seconds How long in-app banners stay visible.
payloadKeys NvpPayloadKeys defaults Key mapping for push payload extraction.
channelId String 'default_channel' Android notification channel ID.
channelName String 'Default Channel' Android notification channel name.
defaultTemplate NvpNotificationTemplate normal Default notification template.
callScreenConfig NvpCallScreenConfig defaults Call screen customization.
defaultGroupKey String? null Default group key for notifications.

Plus 13 optional callbacks: onBannerTap, onBannerDismiss, onSystemNotificationTap, onCallAnswered, onCallDeclined, onCallEnded, onCallIncoming, onCallTimeoutEnded, onCallStateChanged, onTokenRefresh, onPushReceived, onChatPayload, onCallPayload.

NvpNotification #

Field Type Description
title String Notification title (required).
body String Notification body (required).
imageUrl String? Image / avatar URL.
data Map<String, dynamic> Custom data payload.
senderId String? Sender identifier.
senderName String? Sender display name.
senderAvatar String? Sender avatar URL.
receiverId String? Receiver identifier.
receiverName String? Receiver display name.
receiverType String? Receiver type (user, group, etc.).
conversationId String? Conversation / thread identifier.
tag String? Unique tag for in-place updates and cancellation.
unreadMessageCount int? Badge number on the notification.
channelId String? Android channel ID override.
channelName String? Android channel name override.
groupKey String? Group key for notification bundling.
sound String? Custom sound (iOS: bundle file name, Android: raw resource name).

Includes fromMap(), toMap(), and copyWith().

NvpNotificationTemplate #

Field Type Default Description
type NvpNotificationTemplateType Template type (required).
expandedText String? null Expanded text for bigText / richText.
imageUrl String? null Image URL for bigPicture / bigBanner.
summaryText String? null Summary text for bigBanner.
progressValue int? null Current progress value.
progressMax int 100 Maximum progress value.
progressIndeterminate bool false Show indeterminate progress bar.
actions List<NvpNotificationAction>? null Action buttons for interactive.
customLayoutName String? null Android custom XML layout name.
customLayoutData Map? null Data for custom layout.
iosCategoryIdentifier String? null iOS notification category identifier.

Template types: normal, richText, bigText, bigPicture, bigBanner, progress, interactive, custom

NvpNotificationAction #

Field Type Default Description
id String Unique action identifier.
title String Button label.
foreground bool true Bring app to foreground on tap.
isTextInput bool false Show text input field.
textInputPlaceholder String? null Placeholder for text input.

NvpCallConfig #

Field Type Default Description
callId String Unique call session ID (required).
callerName String Caller display name (required).
callerAvatar String? null Caller avatar URL.
isVideo bool false Video call.
isOutgoing bool false Outgoing call.
useNativeScreen bool true Use native UI vs custom Flutter screen.
duration int? null Auto-dismiss timeout in seconds.
ringtone String? null Custom ringtone (iOS: bundle file, Android: raw resource).
extra Map<String, dynamic> {} Extra data.

Includes fromMap(), toMap(), and copyWith().

NvpCallEvent #

Field Type Description
action NvpCallAction? accept, decline, ended, incoming, timeoutEnded
callId String Call session ID.
callerName String? Caller display name.
callerAvatar String? Caller avatar URL.
isVideo bool Video call.
callType String? Call type identifier.
conversationId String? Associated conversation ID.
payload Map<String, dynamic> Extra event data.

NvpCallState #

Field Type Description
callId String Call session ID.
status NvpCallStatus ringing, connected, onHold, ended
isMuted bool Microphone muted.
isSpeakerOn bool Speaker on.
isCameraOn bool Camera on.
connectedAt DateTime? When the call was connected.
extra Map<String, dynamic> Extra state data.

NvpCallScreenConfig #

Field Type Default Description
useNativeCallScreen bool true Native UI vs custom Flutter screen.
callScreenBuilder Widget Function(NvpCallEvent, NvpCallScreenController)? null Custom call screen builder.
backgroundColor Color? null Call screen background color.
avatarPlaceholder Widget? null Placeholder for caller avatar.
onCallInitiated Function? null Called when a call is initiated.
onMuteToggled Function? null Called when mute is toggled.
onSpeakerToggled Function? null Called when speaker is toggled.
onCameraToggled Function? null Called when camera is toggled.

NvpPayloadKeys #

Configurable key mapping so the plugin works with any backend push payload format.

Field Default Description
detailsPath 'data.notificationDetails' Dot-path to notification details in payload.
titleKey 'title' Key for notification title.
bodyKey 'body' Key for notification body.
typeKey 'type' Key for notification type.
senderNameKey 'senderName' Key for sender name.
senderAvatarKey 'senderAvatar' Key for sender avatar URL.
senderIdKey 'sender' Key for sender ID.
callActionKey 'callAction' Key for call action.
sessionIdKey 'sessionId' Key for call session ID.
callTypeKey 'callType' Key for call type.
badgeCountKey 'unreadMessageCount' Key for badge count.
conversationIdKey 'conversationId' Key for conversation ID.
tagKey 'tag' Key for notification tag.
receiverAvatarKey 'receiverAvatar' Key for receiver avatar URL.

NvpLiveActivity — ⚠️ Experimental #

Field Type Default Description
tag String Unique identifier (required).
title String '' Title for Dynamic Island / Lock Screen.
body String '' Body text.
progress double 0.0 Progress (0.0 – 1.0).
icon String? null SF Symbol name.
data Map<String, String> {} Extra data for Widget Extension.

Callback Types #

typedef NvpBannerTapCallback = void Function(NvpNotification notification);
typedef NvpBannerDismissCallback = void Function(NvpNotification notification);
typedef NvpSystemNotificationTapCallback = void Function(NvpNotification notification);
typedef NvpCallAnsweredCallback = void Function(NvpCallEvent event);
typedef NvpCallDeclinedCallback = void Function(NvpCallEvent event);
typedef NvpCallEndedCallback = void Function(NvpCallEvent event);
typedef NvpCallIncomingCallback = void Function(NvpCallEvent event);
typedef NvpCallTimeoutEndedCallback = void Function(NvpCallEvent event);
typedef NvpCallStateChangedCallback = void Function(NvpCallState state);
typedef NvpTokenRefreshCallback = void Function(String token);
typedef NvpPushReceivedCallback = void Function(Map<String, dynamic> payload);
typedef NvpChatPayloadCallback = void Function(NvpNotification notification);
typedef NvpCallPayloadCallback = void Function(NvpCallEvent event);

Example App #

A full example app is included in the example/ directory:

cd example
flutter run

Contributing #

Contributions are welcome. Please open an issue first to discuss what you'd like to change.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/my-feature)
  3. Commit your changes (git commit -am 'Add my feature')
  4. Push to the branch (git push origin feature/my-feature)
  5. Open a Pull Request

License #

MIT — see LICENSE for details.

3
likes
150
points
130
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for notifications (8 templates, grouping) and VoIP calls (CallKit/Telecom, custom Flutter screen) across 6 platforms.

Repository (GitHub)
View/report issues

Topics

#notifications #voip #callkit #push-notifications #flutter-plugin

License

MIT (license)

Dependencies

flutter, flutter_web_plugins, plugin_platform_interface, web

More

Packages that depend on notification_voip_plugin

Packages that implement notification_voip_plugin