{"id":13685946,"url":"https://github.com/hiennguyen92/flutter_callkit_incoming","last_synced_at":"2026-02-23T17:02:31.004Z","repository":{"id":37732678,"uuid":"410387374","full_name":"hiennguyen92/flutter_callkit_incoming","owner":"hiennguyen92","description":"Flutter Callkit Incoming","archived":false,"fork":false,"pushed_at":"2025-04-04T03:43:03.000Z","size":5865,"stargazers_count":196,"open_issues_count":215,"forks_count":375,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-04T04:27:17.650Z","etag":null,"topics":["android","callkit","dart","flutter","incoming","ios"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/flutter_callkit_incoming","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hiennguyen92.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["hiennguyen92"],"patreon":"hiennguyen92","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2021-09-25T21:39:16.000Z","updated_at":"2025-04-03T07:41:31.000Z","dependencies_parsed_at":"2024-01-20T10:29:04.023Z","dependency_job_id":"bf83446e-e3e6-44c9-91ca-00092cceb5a7","html_url":"https://github.com/hiennguyen92/flutter_callkit_incoming","commit_stats":null,"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiennguyen92%2Fflutter_callkit_incoming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiennguyen92%2Fflutter_callkit_incoming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiennguyen92%2Fflutter_callkit_incoming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiennguyen92%2Fflutter_callkit_incoming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hiennguyen92","download_url":"https://codeload.github.com/hiennguyen92/flutter_callkit_incoming/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251824826,"owners_count":21649934,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","callkit","dart","flutter","incoming","ios"],"created_at":"2024-08-02T14:00:59.759Z","updated_at":"2025-10-23T00:11:53.586Z","avatar_url":"https://github.com/hiennguyen92.png","language":"Kotlin","funding_links":["https://github.com/sponsors/hiennguyen92","https://patreon.com/hiennguyen92","https://www.buymeacoffee.com/hiennguyen92"],"categories":["Kotlin"],"sub_categories":[],"readme":"# Flutter Callkit Incoming\n\nA Flutter plugin to show incoming call in your Flutter app (Custom for Android/Callkit for iOS).\n\n[![pub package](https://img.shields.io/pub/v/flutter_callkit_incoming.svg)](https://pub.dev/packages/flutter_callkit_incoming)\n[![pub points](https://img.shields.io/pub/points/flutter_callkit_incoming?label=pub%20points)](https://pub.dev/packages/flutter_callkit_incoming/score)\n[![GitHub stars](https://img.shields.io/github/stars/hiennguyen92/flutter_callkit_incoming.svg?style=social)](https://github.com/hiennguyen92/flutter_callkit_incoming/stargazers)\n[![GitHub forks](https://img.shields.io/github/forks/hiennguyen92/flutter_callkit_incoming.svg?style=social)](https://github.com/hiennguyen92/flutter_callkit_incoming/network)\n[![GitHub license](https://img.shields.io/github/license/hiennguyen92/flutter_callkit_incoming.svg)](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/LICENSE)\n[![Build Status](https://github.com/hiennguyen92/flutter_callkit_incoming/actions/workflows/main.yml/badge.svg)](https://github.com/hiennguyen92/flutter_callkit_incoming/actions/workflows/main.yml)\n\n## Sponsors\n\nOur top sponsors are shown below!\n\n\u003ca href=\"https://getstream.io/video/sdk/flutter/tutorial/video-calling/?utm_source=Github\u0026utm_medium=Github_Repo_Content_Ad\u0026utm_content=Developer\u0026utm_campaign=Github_Video\u0026utm_term=flutter_callkit\" target=\"_blank\"\u003e\n  \u003cimg width=\"250px\" src=\"https://stream-blog.s3.amazonaws.com/blog/wp-content/uploads/fc148f0fc75d02841d017bb36e14e388/Stream-logo-with-background-.png\"/\u003e\n\u003c/a\u003e\n\u003cbr/\u003e\n\u003cspan\u003e\n  \u003ca href=\"https://getstream.io/video/sdk/flutter/tutorial/video-calling/?utm_source=Github\u0026utm_medium=Github_Repo_Content_Ad\u0026utm_content=Developer\u0026utm_campaign=Github_Video\u0026utm_term=flutter_callkit\" target=\"_blank\"\u003eTry the Flutter Video Tutorial 📹\u003c/a\u003e\n\u003c/span\u003e\n\u003c/br\u003e\n\u003c/br\u003e\n\u003ca href=\"https://www.buymeacoffee.com/hiennguyen92\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://cdn.buymeacoffee.com/buttons/default-orange.png\" alt=\"Buy Me A Coffee\" height=\"41\" width=\"174\"\u003e\n\u003c/a\u003e\n\n## ⭐ Features\n\n- Show an incoming call\n- Start an outgoing call\n- Custom UI Android/Callkit for iOS\n- Example using Pushkit/VoIP for iOS\n\n## ⚠️ iOS: ONLY WORKING ON REAL DEVICE\n\n**Please make sure setup/using [PUSHKIT](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/PUSHKIT.md) FOR VOIP**\n\n\u003e **Note:** Please do not use on simulator (Callkit framework not working on simulator)\n\n## 🚀 Installation\n\n### 1. Install Packages\n\nFor version \u003e= v2.5.0, please make sure install and use Java SDK version \u003e= 17 (Android)\n\n**Run this command:**\n```bash\nflutter pub add flutter_callkit_incoming\n```\n\n**Or add to pubspec.yaml:**\n```yaml\ndependencies:\n  flutter_callkit_incoming: ^latest\n```\n\n### 2. Configure Project\n\n#### Android\n\n**AndroidManifest.xml:**\n```xml\n\u003cmanifest...\u003e\n    ...\n    \u003c!-- Using for load image from internet --\u003e\n    \u003cuses-permission android:name=\"android.permission.INTERNET\"/\u003e\n\n    \u003capplication ...\u003e\n        \u003cactivity ...\n            android:name=\".MainActivity\"\n            android:launchMode=\"singleInstance\"\u003e\u003c!-- add this --\u003e\n        ...\n    \u003c/application\u003e\n\u003c/manifest\u003e\n```\n\n**Proguard Rules:**\nThe following rule needs to be added in the `proguard-rules.pro` to avoid obfuscated keys:\n```\n-keep class com.hiennv.flutter_callkit_incoming.** { *; }\n```\n\n#### iOS\n\n**Info.plist:**\n```xml\n\u003ckey\u003eUIBackgroundModes\u003c/key\u003e\n\u003carray\u003e\n    \u003cstring\u003evoip\u003c/string\u003e\n    \u003cstring\u003eremote-notification\u003c/string\u003e\n    \u003cstring\u003eprocessing\u003c/string\u003e \u003c!-- you can add this if needed --\u003e\n\u003c/array\u003e\n```\n\n### 3. Usage\n\n#### Import\n```dart\nimport 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';\n```\n\n#### Show Incoming Call\n```dart\nthis._currentUuid = _uuid.v4();\nCallKitParams callKitParams = CallKitParams(\n  id: _currentUuid,\n  nameCaller: 'Hien Nguyen',\n  appName: 'Callkit',\n  avatar: 'https://i.pravatar.cc/100',\n  handle: '0123456789',\n  type: 0,\n  textAccept: 'Accept',\n  textDecline: 'Decline',\n  missedCallNotification: NotificationParams(\n    showNotification: true,\n    isShowCallback: true,\n    subtitle: 'Missed call',\n    callbackText: 'Call back',\n  ),\n  callingNotification: const NotificationParams(\n    showNotification: true,\n    isShowCallback: true,\n    subtitle: 'Calling...',\n    callbackText: 'Hang Up',\n  ),\n  duration: 30000,\n  extra: \u003cString, dynamic\u003e{'userId': '1a2b3c4d'},\n  headers: \u003cString, dynamic\u003e{'apiKey': 'Abc@123!', 'platform': 'flutter'},\n  android: const AndroidParams(\n    isCustomNotification: true,\n    isShowLogo: false,\n    logoUrl: 'https://i.pravatar.cc/100',\n    ringtonePath: 'system_ringtone_default',\n    backgroundColor: '#0955fa',\n    backgroundUrl: 'https://i.pravatar.cc/500',\n    actionColor: '#4CAF50',\n    textColor: '#ffffff',\n    incomingCallNotificationChannelName: \"Incoming Call\",\n    missedCallNotificationChannelName: \"Missed Call\",\n    isShowCallID: false\n  ),\n  ios: IOSParams(\n    iconName: 'CallKitLogo',\n    handleType: 'generic',\n    supportsVideo: true,\n    maximumCallGroups: 2,\n    maximumCallsPerCallGroup: 1,\n    audioSessionMode: 'default',\n    audioSessionActive: true,\n    audioSessionPreferredSampleRate: 44100.0,\n    audioSessionPreferredIOBufferDuration: 0.005,\n    supportsDTMF: true,\n    supportsHolding: true,\n    supportsGrouping: false,\n    supportsUngrouping: false,\n    ringtonePath: 'system_ringtone_default',\n  ),\n);\nawait FlutterCallkitIncoming.showCallkitIncoming(callKitParams);\n```\n\n\u003e **Note:** For Firebase Message: `@pragma('vm:entry-point')`  \n\u003e https://github.com/firebase/flutterfire/blob/master/docs/cloud-messaging/receive.md#apple-platforms-and-android\n\n#### Request Notification Permission (Android 13+/iOS)\n\nFor Android 13+, please `requestNotificationPermission` or requestPermission of firebase_messaging before `showCallkitIncoming`:\n\n```dart\nawait FlutterCallkitIncoming.requestNotificationPermission({\n  \"title\": \"Notification permission\",\n  \"rationaleMessagePermission\": \"Notification permission is required, to show notification.\",\n  \"postNotificationMessageRequired\": \"Notification permission is required, Please allow notification permission from setting.\"\n});\n```\n\n#### Request Full Intent Permission (Android 14+)\n\nFor Android 14+, please use `canUseFullScreenIntent` and `requestFullIntentPermission`:\n\n```dart\n// Check if can use full screen intent\nawait FlutterCallkitIncoming.canUseFullScreenIntent();\n\n// Request full intent permission\nawait FlutterCallkitIncoming.requestFullIntentPermission();\n```\n\n#### Show Missed Call Notification\n```dart\nthis._currentUuid = _uuid.v4();\nCallKitParams params = CallKitParams(\n  id: _currentUuid,\n  nameCaller: 'Hien Nguyen',\n  handle: '0123456789',\n  type: 1,\n  missedCallNotification: const NotificationParams(\n    showNotification: true,\n    isShowCallback: true,\n    subtitle: 'Missed call',\n    callbackText: 'Call back',\n  ),\n  android: const AndroidParams(\n    isCustomNotification: true,\n    isShowCallID: true,\n  ),\n  extra: \u003cString, dynamic\u003e{'userId': '1a2b3c4d'},\n);\nawait FlutterCallkitIncoming.showMissCallNotification(params);\n```\n\n#### Hide Call Notification (Android)\n```dart\nCallKitParams params = CallKitParams(\n  id: _currentUuid,\n);\nawait FlutterCallkitIncoming.hideCallkitIncoming(params);\n```\n\n#### Start Outgoing Call\n```dart\nthis._currentUuid = _uuid.v4();\nCallKitParams params = CallKitParams(\n  id: this._currentUuid,\n  nameCaller: 'Hien Nguyen',\n  handle: '0123456789',\n  type: 1,\n  extra: \u003cString, dynamic\u003e{'userId': '1a2b3c4d'},\n  ios: IOSParams(handleType: 'generic'),\n  callingNotification: const NotificationParams(\n    showNotification: true,\n    isShowCallback: true,\n    subtitle: 'Calling...',\n    callbackText: 'Hang Up',\n  ),\n  android: const AndroidParams(\n    isCustomNotification: true,\n    isShowCallID: true,\n  )\n);\nawait FlutterCallkitIncoming.startCall(params);\n```\n\n#### End Call\n```dart\n// End specific call\nawait FlutterCallkitIncoming.endCall(this._currentUuid);\n\n// End all calls\nawait FlutterCallkitIncoming.endAllCalls();\n```\n\n#### Get Active Calls\n\niOS: returns active calls from Callkit (only id), Android: only returns last call\n\n```dart\nawait FlutterCallkitIncoming.activeCalls();\n```\n\n**Output:**\n```json\n[{\"id\": \"8BAA2B26-47AD-42C1-9197-1D75F662DF78\", ...}]\n```\n\n#### Set Call Connected\n\nUsed to determine Incoming Call or Outgoing Call status in phone book(reset/start timer):\n\n```dart\nawait FlutterCallkitIncoming.setCallConnected(this._currentUuid);\n```\n\n\u003e **Note:** After the call is ACCEPT or startCall, please call this function. Normally it should be called when WebRTC/P2P is established.\n\n#### Get Device Push Token VoIP\n\niOS: returns deviceToken, Android: returns none\n\n```dart\nawait FlutterCallkitIncoming.getDevicePushTokenVoIP();\n```\n\n**Output:**\n```\nd6a77ca80c5f09f87f353cdd328ec8d7d34e92eb108d046c91906f27f54949cd\n```\n\n\u003e **Important:** Make sure using `SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken)` inside AppDelegate.swift ([Example](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/example/ios/Runner/AppDelegate.swift))\n\n```swift\nfunc pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {\n    print(credentials.token)\n    let deviceToken = credentials.token.map { String(format: \"%02x\", $0) }.joined()\n    // Save deviceToken to your server\n    SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken)\n}\n\nfunc pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {\n    print(\"didInvalidatePushTokenFor\")\n    SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(\"\")\n}\n```\n\n#### Listen Events\n```dart\nFlutterCallkitIncoming.onEvent.listen((CallEvent event) {\n  switch (event!.event) {\n    case Event.actionCallIncoming:\n      // TODO: received an incoming call\n      break;\n    case Event.actionCallStart:\n      // TODO: started an outgoing call\n      // TODO: show screen calling in Flutter\n      break;\n    case Event.actionCallAccept:\n      // TODO: accepted an incoming call\n      // TODO: show screen calling in Flutter\n      break;\n    case Event.actionCallDecline:\n      // TODO: declined an incoming call\n      break;\n    case Event.actionCallEnded:\n      // TODO: ended an incoming/outgoing call\n      break;\n    case Event.actionCallTimeout:\n      // TODO: missed an incoming call\n      break;\n    case Event.actionCallCallback:\n      // TODO: click action `Call back` from missed call notification\n      break;\n    case Event.actionCallToggleHold:\n      // TODO: only iOS\n      break;\n    case Event.actionCallToggleMute:\n      // TODO: only iOS\n      break;\n    case Event.actionCallToggleDmtf:\n      // TODO: only iOS\n      break;\n    case Event.actionCallToggleGroup:\n      // TODO: only iOS\n      break;\n    case Event.actionCallToggleAudioSession:\n      // TODO: only iOS\n      break;\n    case Event.actionDidUpdateDevicePushTokenVoip:\n      // TODO: only iOS\n      break;\n    case Event.actionCallCustom:\n      // TODO: for custom action\n      break;\n  }\n});\n```\n\n#### Call from Native (iOS/Android)\n\n**Swift (iOS):**\n```swift\nvar info = [String: Any?]()\ninfo[\"id\"] = \"44d915e1-5ff4-4bed-bf13-c423048ec97a\"\ninfo[\"nameCaller\"] = \"Hien Nguyen\"\ninfo[\"handle\"] = \"0123456789\"\ninfo[\"type\"] = 1\n// ... set more data\nSwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(flutter_callkit_incoming.Data(args: info), fromPushKit: true)\n\n// Please make sure call `completion()` at the end of the pushRegistry(......, completion: @escaping () -\u003e Void)\n// or `DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { completion() }`\n// if you don't call completion() in pushRegistry(......, completion: @escaping () -\u003e Void), there may be app crash by system when receiving VoIP\n```\n\n**Kotlin (Android):**\n```kotlin\nFlutterCallkitIncomingPlugin.getInstance().showIncomingNotification(...)\n```\n\n**Alternative Swift approach:**\n```swift\nlet data = flutter_callkit_incoming.Data(id: \"44d915e1-5ff4-4bed-bf13-c423048ec97a\", nameCaller: \"Hien Nguyen\", handle: \"0123456789\", type: 0)\ndata.nameCaller = \"Johnny\"\ndata.extra = [\"user\": \"abc@123\", \"platform\": \"ios\"]\n// ... set more data\nSwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true)\n```\n\n**Objective-C:**\n```objc\n#if __has_include(\u003cflutter_callkit_incoming/flutter_callkit_incoming-Swift.h\u003e)\n#import \u003cflutter_callkit_incoming/flutter_callkit_incoming-Swift.h\u003e\n#else\n#import \"flutter_callkit_incoming-Swift.h\"\n#endif\n\nData * data = [[Data alloc]initWithId:@\"44d915e1-5ff4-4bed-bf13-c423048ec97a\" nameCaller:@\"Hien Nguyen\" handle:@\"0123456789\" type:1];\n[data setNameCaller:@\"Johnny\"];\n[data setExtra:@{ @\"userId\" : @\"HelloXXXX\", @\"key2\" : @\"value2\"}];\n// ... set more data\n[SwiftFlutterCallkitIncomingPlugin.sharedInstance showCallkitIncoming:data fromPushKit:YES];\n```\n\n**Send Custom Event from Native:**\n\n**Swift:**\n```swift\nSwiftFlutterCallkitIncomingPlugin.sharedInstance?.sendEventCustom(body: [\"customKey\": \"customValue\"])\n```\n\n**Kotlin:**\n```kotlin\nFlutterCallkitIncomingPlugin.getInstance().sendEventCustom(body: Map\u003cString, Any\u003e)\n```\n\n#### Call API when Accept/Decline/End/Timeout\n#### Setup for Missed call notification(iOS)\n\n**AppDelegate.swift:**\n```swift\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate, CallkitIncomingAppDelegate {\n    \n    override func application(\n        _ application: UIApplication,\n        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n    ) -\u003e Bool {\n        GeneratedPluginRegistrant.register(with: self)\n        \n        // Setup VOIP\n        let mainQueue = DispatchQueue.main\n        let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)\n        voipRegistry.delegate = self\n        voipRegistry.desiredPushTypes = [PKPushType.voIP]\n\n        // Use if using WebRTC\n        // RTCAudioSession.sharedInstance().useManualAudio = true\n        // RTCAudioSession.sharedInstance().isAudioEnabled = false\n\n        //Add for Missed call notification\n        if #available(iOS 10.0, *) {\n          UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate\n        }\n        \n        return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n    }\n\n    // Add for Missed call notification(show notification when foreground)\n    override func userNotificationCenter(_ center: UNUserNotificationCenter,\n                                willPresent notification: UNNotification,\n                                withCompletionHandler completionHandler:\n                                   @escaping (UNNotificationPresentationOptions) -\u003e Void) {\n        \n        CallkitNotificationManager.shared.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)\n    }\n    \n    // Add for Missed call notification(action when click callback in missed notification)\n    override func userNotificationCenter(_ center: UNUserNotificationCenter,\n                                         didReceive response: UNNotificationResponse,\n                                         withCompletionHandler completionHandler: @escaping () -\u003e Void) {\n        if response.actionIdentifier == CallkitNotificationManager.CALLBACK_ACTION {\n            let data = response.notification.request.content.userInfo as? [String: Any]\n            SwiftFlutterCallkitIncomingPlugin.sharedInstance?.sendCallbackEvent(data)\n        }\n        completionHandler()\n    }\n\n    // Func Call API for Accept\n    func onAccept(_ call: Call, _ action: CXAnswerCallAction) {\n        let json = [\"action\": \"ACCEPT\", \"data\": call.data.toJSON()] as [String: Any]\n        print(\"LOG: onAccept\")\n        self.performRequest(parameters: json) { result in\n            switch result {\n            case .success(let data):\n                print(\"Received data: \\(data)\")\n                // Make sure call action.fulfill() when you are done (connected WebRTC - Start counting seconds)\n                action.fulfill()\n            case .failure(let error):\n                print(\"Error: \\(error.localizedDescription)\")\n            }\n        }\n    }\n    \n    // Func Call API for Decline\n    func onDecline(_ call: Call, _ action: CXEndCallAction) {\n        let json = [\"action\": \"DECLINE\", \"data\": call.data.toJSON()] as [String: Any]\n        print(\"LOG: onDecline\")\n        self.performRequest(parameters: json) { result in\n            switch result {\n            case .success(let data):\n                print(\"Received data: \\(data)\")\n                // Make sure call action.fulfill() when you are done\n                action.fulfill()\n            case .failure(let error):\n                print(\"Error: \\(error.localizedDescription)\")\n            }\n        }\n    }\n    \n    // Func Call API for End\n    func onEnd(_ call: Call, _ action: CXEndCallAction) {\n        let json = [\"action\": \"END\", \"data\": call.data.toJSON()] as [String: Any]\n        print(\"LOG: onEnd\")\n        self.performRequest(parameters: json) { result in\n            switch result {\n            case .success(let data):\n                print(\"Received data: \\(data)\")\n                // Make sure call action.fulfill() when you are done\n                action.fulfill()\n            case .failure(let error):\n                print(\"Error: \\(error.localizedDescription)\")\n            }\n        }\n    }\n    \n    func onTimeOut(_ call: Call) {\n        let json = [\"action\": \"TIMEOUT\", \"data\": call.data.toJSON()] as [String: Any]\n        print(\"LOG: onTimeOut\")\n        self.performRequest(parameters: json) { result in\n            switch result {\n            case .success(let data):\n                print(\"Received data: \\(data)\")\n            case .failure(let error):\n                print(\"Error: \\(error.localizedDescription)\")\n            }\n        }\n    }\n\n    func didActivateAudioSession(_ audioSession: AVAudioSession) {\n        // Use if using WebRTC\n        // RTCAudioSession.sharedInstance().audioSessionDidActivate(audioSession)\n        // RTCAudioSession.sharedInstance().isAudioEnabled = true\n    }\n    \n    func didDeactivateAudioSession(_ audioSession: AVAudioSession) {\n        // Use if using WebRTC\n        // RTCAudioSession.sharedInstance().audioSessionDidDeactivate(audioSession)\n        // RTCAudioSession.sharedInstance().isAudioEnabled = false\n    }\n}\n```\n\n\u003e **Please check full example:** [Example](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/example/ios/Runner/AppDelegate.swift)\n\n**MainActivity.kt:**\n```kotlin\nclass MainActivity: FlutterActivity(){\n\n    private var callkitEventCallback = object: CallkitEventCallback{\n        override fun onCallEvent(event: CallkitEventCallback.CallEvent, callData: Bundle) {\n            when (event) {\n                CallkitEventCallback.CallEvent.ACCEPT -\u003e {\n                    // Do something with answer\n                }\n                CallkitEventCallback.CallEvent.DECLINE -\u003e {\n                    // Do something with decline\n                }\n            }\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        FlutterCallkitIncomingPlugin.registerEventCallback(callkitEventCallback)\n    }\n\n    override fun onDestroy() {\n        FlutterCallkitIncomingPlugin.unregisterEventCallback(callkitEventCallback)\n        super.onDestroy()\n    }\n\n\n}\n```\n\n\u003e **Please check full example:** [Example](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/example/android/app/src/main/kotlin/com/example/flutter_callkit_incoming_example/MainActivity.kt\n)\n\n## 📋 Properties\n\n### Main Properties\n\n| Property | Description | Default |\n|----------|-------------|---------|\n| **`id`** | UUID identifier for each call. UUID should be unique for every call and when the call is ended, the same UUID for that call to be used. Suggest using [uuid](https://pub.dev/packages/uuid). ACCEPT ONLY UUID | Required |\n| **`nameCaller`** | Caller's name | _None_ |\n| **`appName`** | App's name. Used for display inside Callkit (iOS). | App Name, `Deprecated for iOS \u003e 14, default using App name` |\n| **`avatar`** | Avatar's URL used for display for Android. `/android/src/main/res/drawable-xxxhdpi/ic_default_avatar.png` | _None_ |\n| **`handle`** | Phone number/Email/Any | _None_ |\n| **`type`** | 0 - Audio Call, 1 - Video Call | `0` |\n| **`duration`** | Incoming call/Outgoing call display time (second). If the time is over, the call will be missed | `30000` |\n| **`textAccept`** | Text `Accept` used in Android | `Accept` |\n| **`textDecline`** | Text `Decline` used in Android | `Decline` |\n| **`extra`** | Any data added to the event when received | `{}` |\n| **`headers`** | Any data for custom header avatar/background image | `{}` |\n| **`missedCallNotification`** | Android data needed to customize Missed Call Notification | Below |\n| **`callingNotification`** | Android data needed to customize Calling Notification | Below |\n| **`android`** | Android data needed to customize UI | Below |\n| **`ios`** | iOS data needed | Below |\n\n### Missed Call Notification\n\n| Property | Description | Default |\n|----------|-------------|---------|\n| **`subtitle`** | Text `Missed Call` used in Android/iOS (show in missed call notification) | `Missed Call` |\n| **`callbackText`** | Text `Call back` used in Android/iOS (show in missed call notification action) | `Call back` |\n| **`showNotification`** | Show missed call notification when timeout | `true` |\n| **`isShowCallback`** | Show callback action from missed call notification | `true` |\n\n### Calling Notification\n\n| Property | Description | Default |\n|----------|-------------|---------|\n| **`subtitle`** | Text used in Android (show in calling notification) | `Calling...` |\n| **`callbackText`** | Text used in Android (show in calling notification action) | `Hang Up` |\n| **`showNotification`** | Show calling notification when start call/accept call | `true` |\n| **`isShowCallback`** | Show hang up action from calling notification | `true` |\n\n### Android\n\n| Property | Description | Default |\n|----------|-------------|---------|\n| **`isCustomNotification`** | Using custom notifications | `false` |\n| **`isCustomSmallExNotification`** | Using custom notification small on some devices clipped out in Android | `false` |\n| **`isShowLogo`** | Show logo app inside full screen. `/android/src/main/res/drawable-xxxhdpi/ic_logo.png` | `false` |\n| **`logoUrl`** | Logo app inside full screen. Example: http://... https://... or \"assets/abc.png\" | _None_ |\n| **`ringtonePath`** | File name of a ringtone ex: `ringtone_default`. Put file into `/android/app/src/main/res/raw/ringtone_default.mp3` | `system_ringtone_default` \u003cbr\u003eusing ringtone default of the phone |\n| **`backgroundColor`** | Incoming call screen background color | `#0955fa` |\n| **`backgroundUrl`** | Using image background for Incoming call screen. Example: http://... https://... or \"assets/abc.png\" | _None_ |\n| **`actionColor`** | Color used in button/text on notification | `#4CAF50` |\n| **`textColor`** | Color used for the text in full screen notification | `#ffffff` |\n| **`incomingCallNotificationChannelName`** | Notification channel name of incoming call | `Incoming call` |\n| **`missedCallNotificationChannelName`** | Notification channel name of missed call | `Missed call` |\n| **`isShowCallID`** | Show call id app inside full screen/notification | `false` |\n| **`isShowFullLockedScreen`** | Show full screen on Locked Screen (please make sure call `requestFullIntentPermission` for Android 14+) | `true` |\n\n### iOS\n\n| Property | Description | Default |\n|----------|-------------|---------|\n| **`iconName`** | App's Icon. Used for display inside Callkit (iOS) | `CallKitLogo` \u003cbr\u003e using from `Images.xcassets/CallKitLogo` |\n| **`handleType`** | Type handle call `generic`, `number`, `email`, Recommended to use `generic` for more reasonable callkit display | `generic` |\n| **`supportsVideo`** | | `true` |\n| **`maximumCallGroups`** | | `2` |\n| **`maximumCallsPerCallGroup`** | | `1` |\n| **`audioSessionMode`** | | _None_, `gameChat`, `measurement`, `moviePlayback`, `spokenAudio`, `videoChat`, `videoRecording`, `voiceChat`, `voicePrompt` |\n| **`audioSessionActive`** | | `true` |\n| **`audioSessionPreferredSampleRate`** | | `44100.0` |\n| **`audioSessionPreferredIOBufferDuration`** | | `0.005` |\n| **`supportsDTMF`** | | `true` |\n| **`supportsHolding`** | | `true` |\n| **`supportsGrouping`** | | `true` |\n| **`supportsUngrouping`** | | `true` |\n| **`ringtonePath`** | Add file to root project xcode `/ios/Runner/Ringtone.caf` and Copy Bundle Resources (Build Phases) | `Ringtone.caf`\u003cbr\u003e`system_ringtone_default` \u003cbr\u003eusing ringtone default of the phone |\n\n## 📁 Source Code\n\nPlease checkout repo GitHub:\n- [https://github.com/hiennguyen92/flutter_callkit_incoming](https://github.com/hiennguyen92/flutter_callkit_incoming)\n- [Example](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/example/lib/main.dart)\n\n## 📱 Pushkit - Received VoIP and Wake App from Terminated State (iOS Only)\n\nPlease check [PUSHKIT.md](https://github.com/hiennguyen92/flutter_callkit_incoming/blob/master/PUSHKIT.md) for setup Pushkit for iOS.\n\n## 📋 Todo\n\n- [ ] Run background\n- [ ] Simplify the setup process\n- [X] Custom notification for iOS (Missing notification)\n- [X] Keep notification when calling\n\n## 🎯 Demo\n\n### Demo Illustration\n\n### Images\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cstrong\u003eiOS (Lockscreen)\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cstrong\u003eiOS (Full Screen)\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cstrong\u003eiOS (Alert)\u003c/strong\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/hiennguyen92/flutter_callkit_incoming/master/images/image1.png\" width=\"220\" alt=\"iOS Lockscreen\"\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/hiennguyen92/flutter_callkit_incoming/master/images/image2.png\" width=\"220\" alt=\"iOS Full Screen\"\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/hiennguyen92/flutter_callkit_incoming/master/images/image3.png\" width=\"220\" alt=\"iOS Alert\"\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cstrong\u003eAndroid (Lockscreen) - Audio\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cstrong\u003eAndroid (Alert) - Audio\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cstrong\u003eAndroid (Lockscreen) - Video\u003c/strong\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/hiennguyen92/flutter_callkit_incoming/master/images/image4.jpg\" width=\"220\" alt=\"Android Lockscreen Audio\"\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/hiennguyen92/flutter_callkit_incoming/master/images/image5.jpg\" width=\"220\" alt=\"Android Alert Audio\"\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/hiennguyen92/flutter_callkit_incoming/master/images/image6.jpg\" width=\"220\" alt=\"Android Lockscreen Video\"\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cstrong\u003eAndroid (Alert) - Video\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cstrong\u003eisCustomNotification: false\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/hiennguyen92/flutter_callkit_incoming/master/images/image7.jpg\" width=\"220\" alt=\"Android Alert Video\"\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://raw.githubusercontent.com/hiennguyen92/flutter_callkit_incoming/master/images/image8.jpg\" width=\"220\" alt=\"Custom Notification False\"\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhiennguyen92%2Fflutter_callkit_incoming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhiennguyen92%2Fflutter_callkit_incoming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhiennguyen92%2Fflutter_callkit_incoming/lists"}