flutter_callkeep 0.3.4 copy "flutter_callkeep: ^0.3.4" to clipboard
flutter_callkeep: ^0.3.4 copied to clipboard

iOS CallKit and Android ConnectionService bindings for Flutter

callkeep #

Showing incoming call notification/screen using iOS CallKit and Android Custom UI for Flutter

Native setup #

flutter_callkeep requires the following permissions.

Android #

No extra setup is needed

iOS #

in Info.plist

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

Then you need to update AppDelegate.swift to follow the example for handling PushKit as push handling must be done through native iOS code due to iOS 13 PushKit VoIP restrictions.

import UIKit
import PushKit
import Flutter
import flutter_callkeep

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        
        //Setup VOIP
        let mainQueue = DispatchQueue.main
        let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
        voipRegistry.delegate = self
        voipRegistry.desiredPushTypes = [PKPushType.voIP]
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    // Handle updated push credentials
    func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
        print(credentials.token)
        let deviceToken = credentials.token.map { String(format: "%02x", $0) }.joined()
        print(deviceToken)
        //Save deviceToken to your server
        SwiftCallKeepPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken)
    }
    
    func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
        print("didInvalidatePushTokenFor")
        SwiftCallKeepPlugin.sharedInstance?.setDevicePushTokenVoIP("")
    }
    
    // Handle incoming pushes
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
        print("didReceiveIncomingPushWith")
        guard type == .voIP else { return }
        
        let id = payload.dictionaryPayload["id"] as? String ?? ""
        let callerName = payload.dictionaryPayload["callerName"] as? String ?? ""
        let userId = payload.dictionaryPayload["callerId"] as? String ?? ""
        let handle = payload.dictionaryPayload["handle"] as? String ?? ""
        let isVideo = payload.dictionaryPayload["isVideo"] as? Bool ?? false
        
        let data = flutter_callkeep.Data(id: id, callerName: callerName, handle: handle, hasVideo: isVideo)
        //set more data
        data.extra = ["userId": callerId, "platform": "ios"]
        data.appName = "Done"
        //data.iconName = ...
        //data.....
        SwiftCallKeepPlugin.sharedInstance?.displayIncomingCall(data, fromPushKit: true)
    }   
}

Usage #

Setup: #

You need to have base CallKeep config setup to reduce code duplication and make it easier to display incoming calls:

 final callKeepBaseConfig = CallKeepBaseConfig(
      appName: 'Done',
      androidConfig: CallKeepAndroidConfig(
        logo: 'logo',
        notificationIcon: 'notification_icon',
        ringtoneFileName: 'ringtone.mp3',
        accentColor: '#34C7C2',
      ),
      iosConfig: CallKeepIosConfig(
        iconName: 'Icon',
        maximumCallGroups: 1,
      ),
    );

Display incoming call: #

// Config and uuid are the only required parameters
final config = CallKeepIncomingConfig.fromBaseConfig(
    config: callKeepBaseConfig,
    uuid: uuid,
    contentTitle: 'Incoming call from Done',
    hasVideo: hasVideo,
    handle: handle,
    callerName: incomingCallUsername,
    extra: callData,
);
await CallKeep.instance.displayIncomingCall(config);

Show missed call notification (Android only): #

// config and uuid are the only required parameters
final config = CallKeepIncomingConfig.fromBaseConfig(
    config: callKeepBaseConfig,
    uuid: uuid,
    contentTitle: 'Incoming call from Done',
    hasVideo: hasVideo,
    handle: handle,
    callerName: incomingCallUsername,
    extra: callData,
);
await CallKeep.instance.showMissCallNotification(config);

Start an outgoing call: #

// config and uuid are the only required parameters
final config = CallKeepOutgoingConfig.fromBaseConfig(
    config: DoneCallsConfig.instance.callKeepBaseConfig,
    uuid: uuid,
    handle: handle,
    hasVideo: hasVideo ?? false,
);
CallKeep.instance.startCall(config);

Handling events: #

CallKeep.instance.onEvent.listen((event) async {
    // TODO: Implement other events
    if (event == null) return;
    switch (event.type) {
        case CallKeepEventType.callAccept:
        final data = event.data as CallKeepCallData;
        print('call answered: ${data.toMap()}');
        NavigationService.instance
            .pushNamedIfNotCurrent(AppRoute.callingPage, args: data.toMap());
        if (callback != null) callback.call(event);
        break;
        case CallKeepEventType.callDecline:
        final data = event.data as CallKeepCallData;
        print('call declined: ${data.toMap()}');
        await requestHttp("ACTION_CALL_DECLINE_FROM_DART");
        if (callback != null) callback.call(data);
        break;
        default:
        break;
    }
});

Customization (Android): #

You can customize background color and add localizations to text through adding the values to your '{{yourApp}}/android/app/src/main/res/values' and '{{yourApp}}/android/app/src/main/res/values-{{languageCode}}' for localizations.

The main values are: in colors.xml

    <!-- A hex color value to be displayed on the top part of the custom incoming call UI --> 
    <color name="incoming_call_bg_color">#80ffffff</color>

in strings.xml

    <!-- Accept button call text, useful for localization --> 
    <string name="accept_text">Accept</string>
    <!-- Decline button call text, useful for localization --> 
    <string name="decline_text">Decline</string>
    <!-- Missed call text, useful for localization --> 
    <string name="text_missed_call">Missed call</string>
    <!-- Callback button text, useful for localization --> 
    <string name="text_call_back">Call back</string>
    <!-- Incoming call custom UI header, useful for localization -->
    <!-- This can be set from Flutter as well when displaying incoming call --> 
    <string name="call_header">Call from CallKeep</string>
80
likes
160
pub points
89%
popularity

Publisher

verified publisherdoneservices.co

iOS CallKit and Android ConnectionService bindings for Flutter

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_callkeep