{"id":18438268,"url":"https://github.com/easemob/flutter_chat_callkit","last_synced_at":"2025-04-14T13:16:01.456Z","repository":{"id":156058518,"uuid":"613167878","full_name":"easemob/flutter_chat_callkit","owner":"easemob","description":null,"archived":false,"fork":false,"pushed_at":"2023-06-09T03:29:39.000Z","size":250,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":9,"default_branch":"dev","last_synced_at":"2024-12-24T23:24:50.520Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/easemob.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"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}},"created_at":"2023-03-13T03:01:11.000Z","updated_at":"2024-07-23T14:52:41.000Z","dependencies_parsed_at":null,"dependency_job_id":"520b9ff5-c827-4c8d-bf2f-f77e9d99ef17","html_url":"https://github.com/easemob/flutter_chat_callkit","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easemob%2Fflutter_chat_callkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easemob%2Fflutter_chat_callkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easemob%2Fflutter_chat_callkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/easemob%2Fflutter_chat_callkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/easemob","download_url":"https://codeload.github.com/easemob/flutter_chat_callkit/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239120196,"owners_count":19584980,"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":[],"created_at":"2024-11-06T06:19:18.193Z","updated_at":"2025-02-16T10:43:38.051Z","avatar_url":"https://github.com/easemob.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Get Started with Agora Chat CallKit for Flutter\n\n`agora_chat_callkit` is a video and audio component library built on top of `agora_chat_sdk` and `agora_rtc_engine`. It provides logical modules for making and receiving calls, including one-to-one voice calls, one-to-one video calls, group audio calls, and group video calls. It uses `agora_chat_sdk` to handle call invitations and negotiations. After negotiations are complete, the `AgoraChatCallManager.setRTCTokenHandler` callback is triggered, and the Agora RTC token needs to be returned. The Agora RTC token must be provided by the developer.\n\n## Understand the tech\n\nFor a call, the call invitation is implemented via Agora Chat, while the call is made through Agora RTC. As the accounts of Agora RTC and Agora Chat are not globally recognizable at present, the accounts need to be mapped via the `AgoraChatCallManager.setUserMapperHandler` callback in `agora_chat_callkit`. When a user joins the call, the Agora RTC user ID (UID) will be returned via the callback. After you get the corresponding Agora Chat user ID, you need to return it to `agora_chat_callkit`. If there is no mapping between the two user IDs, the call will not proceed properly. See `AgoraChatCallUserMapper`.\n\nThis section describes how to implement a one-to-one call or group call. \n\n\u003cdiv class=\"alert note\"\u003eThe `AgoraChatCallManager.initRTC` method is called before a call is made or answered.\u003c/div\u003e\n\nThe basic process for implementing a one-to-one audio or video call is as follows:\n\n1. The caller calls the `AgoraChatCallManager.startSingleCall` method to invite the callee to join the call. \n2. The callee receives the call invitation through the `AgoraChatCallKitEventHandler.onReceiveCall` callback and handles the call:\n   - To answer the call, the callee calls the `AgoraChatCallManager.answer` method. The other party receives the `AgoraChatCallKitEventHandler.onUserJoined` event and the call starts.\n   - To hang up the call, the callee calls the `AgoraChatCallManager.releaseRTC` method. The other party receives the `AgoraChatCallKitEventHandler.onCallEnd` event.\n\nThe basic process for implementing a group audio or video call is as follows:   \n\n1. The caller calls the `AgoraChatCallManager.startInviteUsers` method to invite multiple users to join the call. \n2. The callee receives the call invitation through the `AgoraChatCallKitEventHandler.onReceiveCall` event and handles the call:\n  - To answer the call, the callee calls the `AgoraChatCallManager.answer` method. The other parties receive the `AgoraChatCallKitEventHandler.onUserJoined` event and the call starts.\n  - To hang up the call, the callee calls the `AgoraChatCallManager.releaseRTC` method. Group calls do not end automatically, and therefore the users need to call this method to hang up the call. \n\n\u003cdiv class=\"alert note\"\u003eWhen users join or leave the call, the UI should be modified accordingly.\u003c/div\u003e \n\n## Prerequisites\n\nIn order to follow the procedure in this page, you must have the following:\n\n- A valid Agora [account](https://docs.agora.io/en/video-calling/reference/manage-agora-account/#create-an-agora-account)\n- An Agora [project](https://docs.agora.io/en/video-calling/reference/manage-agora-account/#create-an-agora-project) with an [App Key](https://docs.agora.io/en/agora-chat/get-started/enable#get-the-information-of-the-chat-project) that has [enabled the Chat service](https://docs.agora.io/en/agora-chat/get-started/enable)\n\nIf your target platform is iOS, your development environment must meet the following requirements:\n- Flutter 2.10 or later\n- Dart 2.16 or later\n- macOS\n- Xcode 12.4 or later with Xcode Command Line Tools\n- CocoaPods\n- An iOS simulator or a real iOS device running iOS 10.0 or later\n\nIf your target platform is Android, your development environment must meet the following requirements:\n- Flutter 2.10 or later\n- Dart 2.16 or later\n- macOS or Windows\n- Android Studio 4.0 or later with JDK 1.8 or later\n- An Android simulator or a real Android device running Android SDK API level 21 or later\n\n\u003cdiv class=\"alert note\"\u003eYou can run \u003ccode\u003eflutter doctor\u003c/code\u003e to see if there are any platform dependencies you need to complete the setup.\u003c/div\u003e\n\n## Project setup\n\n### Add the dependencies\n\nAdd the following dependencies in `pubspec.yaml`:\n\n```\n  agora_chat_sdk: 1.1.0\n  agora_rtc_engine: 6.1.0\n```\n\n### Add project permissions\n\n#### Android\n\n```\n\u003cmanifest\u003e\n...\n\u003cuses-permission android:name=\"android.permission.READ_PHONE_STATE\" /\u003e\n\u003cuses-permission android:name=\"android.permission.INTERNET\" /\u003e\n\u003cuses-permission android:name=\"android.permission.RECORD_AUDIO\" /\u003e\n\u003cuses-permission android:name=\"android.permission.CAMERA\" /\u003e\n\u003cuses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" /\u003e\n\u003cuses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" /\u003e\n\u003c!-- The Agora SDK requires Bluetooth permissions in case users are using Bluetooth devices.--\u003e\n\u003cuses-permission android:name=\"android.permission.BLUETOOTH\" /\u003e\n\u003cuses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" /\u003e\n\u003cuses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" /\u003e\n\u003cuses-permission android:name=\"android.permission.WAKE_LOCK\"/\u003e\n...\n\u003c/manifest\u003e\n```\n\n#### iOS\n\nAdd the following lines to **info.plist**:\n\n\n|Key|Type|Value|\n---|---|---\n`Privacy - Microphone Usage Description` | String | For microphone access\n`Privacy - Camera Usage Description` | String | For camera access\n\n\n### Prevent code obfuscation\n\nIn the example/android/app/proguard-rules.pro file, add the following lines to prevent code obfuscation: \u003c/application\u003e\n\n```\n-keep class com.hyphenate.** {*;}\n-dontwarn  com.hyphenate.**\n```\n\n\n## Implement audio and video calling\n\nYou need to make sure that the Agora Chat SDK is initialized before calling AgoraChatCallKit and AgoraChatCallKit widget at the top of your widget tree. You can add it in the `MaterialApp` builder.\n\n```\nimport 'package:agora_chat_callkit/agora_chat_callkit.dart';\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Flutter Demo',\n      theme: ThemeData(\n        primarySwatch: Colors.blue,\n      ),\n      builder: (context, child){\n         return AgoraChatCallKit(\n            agoraAppId: \u003c--Add Your Agora App Id Here--\u003e,\n            child: child!,\n          );\n      },\n      home: const MyHomePage(title: 'Flutter Demo'),\n    );\n  }\n}\n```\n\n### Add the Agora token callback\n\nAgora RTC needs a token and a channel ID to join a channel. Therefore, the two parameters are required when `agora_chat_callkit` is used. `agora_chat_callkit` gets the two parameters from the `AgoraChatCallManager.setRTCTokenHandler` callback.\n\n```\n// channel: The channel to join.\n// agoraAppId: The Agora app ID.\n// agoraUid: The user ID (UID) of Agora RTC.\nAgoraChatCallManager.setRTCTokenHandler((channel, agoraAppId, agoraUid) {\n  // agoraToken: The token of the Agora RTC user.\n  // agoraUid: The user ID of Agora RTC.\n  return Future(() =\u003e {agoraToken, agoraUid});\n});\n```\n\n### Add the user mapping callback\n\nSet the callback of the mapping between the Agora RTC user ID and Agora Chat user ID.\n\n```\n// channel: The channel to which the Agora RTC user ID belongs.\n// agoraUid: The Agora RTC user ID that corresponds to the Agora Chat user ID.\nAgoraChatCallManager.setUserMapperHandler((channel, agoraUid) {\n  // channel: The channel to which the Agora RTC user ID belongs.\n  // agoraUid: The Agora RTC user ID that corresponds to the Agora Chat user ID.\n  // userId: The Agora Chat user ID that corresponds to the Agora RTC user ID.\n  return Future(() =\u003e AgoraChatCallUserMapper(channel, {agoraUid, userId}));\n});\n```\n\n### Listen for callback events\n\nAdd a `AgoraChatCallKitEventHandler` listener by using the `AgoraChatCallManager.addEventListener` method. Call `AgoraChatCallManager.removeEventListener` to remove the listener when not in use.\n\n```\nAgoraChatCallManager.addEventListener(\n  // Handler key. This key is used to ensure that the handler is unique.\n  // This key is required when deleting the handler.\n  UNIQUE_HANDLER_ID,\n  // CallKit EventHandler.\n  AgoraChatCallKitEventHandler(),\n);\n```\n\n`AgoraChatCallKitEventHandler` is described as follows:\n\n```\n  /// AgoraChatCallKit event handler.\n  ///\n  /// Param [onError] Occurs when the call fails. See [AgoraChatCallError].\n  ///\n  /// Param [onCallEnd] Occurs when the call ends. See [AgoraChatCallEndReason].\n  ///\n  /// Param [onReceiveCall] Occurs when a call invitation is received.\n  ///\n  /// Param [onJoinedChannel] Occurs when the current user joins the call.\n  /// \n  /// Param [onUserLeaved] Occurs when an active user leaves.\n  ///\n  /// Param [onUserJoined] Occurs when a user joins a call.\n  ///\n  /// Param [onUserMuteAudio] Occurs when the peer's mute status changes during an audio call.\n  ///\n  /// Param [onUserMuteVideo] Occurs when the peer's camera status changes during a video call.\n  ///\n  /// Param [onUserRemoved] Occurs when the callee rejects the call or the call times out.\n  ///\n  /// Param [onAnswer] Occurs when the call is answered.\n  ///\n  AgoraChatCallKitEventHandler({\n    this.onError,\n    this.onCallEnd,\n    this.onReceiveCall,\n    this.onJoinedChannel,\n    this.onUserLeaved,\n    this.onUserJoined,\n    this.onUserMuteAudio,\n    this.onUserMuteVideo,\n    this.onUserRemoved,\n    this.onAnswer,\n  });\n```\n\n| Event             | Description                    |\n| :---------------- | :----------------------- |\n| final void Function(AgoraChatCallError error)? onError       | Occurs when the call fails. For example, the callee fails to join the channel or the call invitation fails to be sent. The operator receives the event. This event is applicable to one-to-one calls and group calls. See `AgoraChatCallError`.  |\n| final void Function(String? callId, AgoraChatCallEndReason reason)? onCallEnd | Occurs when the call ends. This event is applicable only to one-to-one calls. Both the caller and callee receive this event. See `AgoraChatCallEndReason`.  |\n| final void Function(int agoraUid, String? userId)? onUserLeaved | Occurs when an active user leaves. This event is applicable only to group calls. All other users in the call receive this event. In this event, `agoraUid` indicates the Agora RTC user ID and `userId` indicates the Agora Chat user ID. |\n| final void Function(int agoraUid, String? userId)? onUserJoined | Occurs when a user joins a call. The user that joins the call receives this event. This event is applicable only to group calls. In this event, `agoraUid` indicates the Agora RTC user ID and `userId` indicates the Agora Chat user ID. |\n| final void Function(String channel)? onJoinedChannel         | Occurs when the current user joins the call. This event is applicable only to group calls. All other users in the call receive this event. In this event, `channel` indicates the channel ID. |\n| final void Function(String callId)? onAnswer                 | Occurs when the call is answered. This event is applicable only to one-to-one calls. Both the caller and callee receive this event. |\n| final void Function(String userId, String callId, AgoraChatCallType callType, Map\u003cString, String\u003e? ext)? onReceiveCall | Occurs when a call invitation is received. This event is applicable to both one-to-one calls and groups calls. The callee receives this event. In this event, `userId` indicates the Agora Chat user ID of the caller, `callId` indicates the ID of the current call, and `callType` indicates the current call type. See `AgoraChatCallType`. |\n| final void Function(int agoraUid, bool muted)? onUserMuteAudio | Occurs when the microphone status of the peer user changes. This event is applicable to both one-to-one calls and groups calls. The peer user in one-to-one calls or other users in group calls receive this event. In this event, `agoraUid` indicates the Agora RTC user ID of the peer user and `muted` indicates whether the peer microphone is muted or not. |\n| final void Function(int agoraUid, bool muted)? onUserMuteVideo | Occurs when the camera status of the peer user changes. This event is applicable to both one-to-one calls and groups calls. The peer user in one-to-one calls or other users in group calls receive this event. In this event, `agoraUid` indicates the Agora RTC user ID of the peer user and `muted` indicates whether the peer camera is disabled or not. |\n| final void Function(String callId, String userId, AgoraChatCallEndReason reason)? onUserRemoved | Occurs when the callee rejects the call or the call times out. This event is applicable only to groups calls. The caller receives this event. In this event, `callId` indicates the current call ID, `userId` indicates the Agora Chat user ID of the callee, and `reason` indicates the hangup reason. See `AgoraChatCallEndReason`. |\n\n### Start a call \n\nBefore making or answering a call, you need to first call the `AgoraChatCallManager.initRTC` method to initialize Agora RTC. \n\n#### Start a one-to-one call\n\nCall the `AgoraChatCallManager.startSingleCall` method to make a one-to-one call. This method returns the `callId` parameter which can be used by the caller to hang up the call. The callee receives the `onReceiveCall` event.\n\n```\nawait AgoraChatCallManager.initRTC();\ntry {\n  // userId: The Agora Chat user ID of the callee.\n  // type: The call type, which can be `AgoraChatCallType.audio_1v1` or `AgoraChatCallType.video_1v1`. \n  String callId = await AgoraChatCallManager.startSingleCall(\n    userId,\n    type: AgoraChatCallType.audio_1v1,\n  );\n} on AgoraChatCallError catch (e) {\n  ...\n}\n```\n\n#### Start a group call\n\nTo make a group call, you can call the `await AgoraChatCallManager.startInviteUsers` method to invite users to join\n the call. This method returns the `callId` parameter which can be used by the caller to hang up the call. The callees receive the `onReceiveCall` event.\n\n```\nawait AgoraChatCallManager.initRTC();\ntry {\n  // userList: The Agora Chat user IDs of the callees.\n  String callId = await AgoraChatCallManager.startInviteUsers(userList);\n} on AgoraChatCallError catch (e) {\n  ...\n}\n```\n\n### Receive a call\n\nTo listen for the received call invitation, the users need to first add a `AgoraChatCallKitEventHandler` listener by using the `AgoraChatCallManager.addEventListener` method. Call `AgoraChatCallManager.removeEventListener` to remove the listener when not in use.\n\nIn either a one-to-one call or group call, once a call invitation is sent, the callee receives the invitation in the `onReceiveCall` callback. The audio or video page can be displayed, depending on the call type.\n\n```\nAgoraChatCallManager.addEventListener(\n  // Handler key. This key is used to ensure that the handler is unique.\n  // This key is required when deleting the handler.\n  UNIQUE_HANDLER_ID,\n  AgoraChatCallKitEventHandler(\n    // Occurs when you receive a call invitation.\n    onReceiveCall(String userId, String callId, AgoraChatCallType callType, Map\u003cString, String\u003e? ext) {\n      // Receive a call.\n    }\n  ),\n);\n```\n\nThe callee needs to choose whether to answer or reject the call:\n\n- To answer a call, call the `AgoraChatCallManager.initRTC` method first and then the `answer` method.\n\nIn a one-to-one call, both the caller and callee receive the `onAnswer` event. In a group call, the new user that joins the call receives the `onUserJoined` event and other users in the call receive the `onJoinedChannel` event.\n\n```\nawait AgoraChatCallManager.initRTC();\ntry {\n  // callId: The call ID which can be obtained from the onReceiveCall callback.\n  await AgoraChatCallManager.answer(callId);\n} on AgoraChatCallError catch (e) {\n  ...\n}\n```\n\n- To reject the call, call the `AgoraChatCallManager.releaseRTC` method:\n\nFor a one-to-one call, the caller receives the `onError` event. For a group call, other users than the callee receive the `onUserRemoved` event.\n\n```\ntry {\n  // callId: The call ID which can be obtained via the `onReceiveCall` callback.\n  await AgoraChatCallManager.hangup(callId);\n} on AgoraChatCallError catch (e) {\n  ...\n}\nawait AgoraChatCallManager.releaseRTC();\n```\n\n### End the call\n\nA one-to-one call ends as soon as one of the two users hangs up, while a group call ends only after the local user hangs up.\n\nFor a one-to-one call, either the caller or callee can call the `AgoraChatCallManager.releaseRTC` method to end the call. When one party ends the call, the other party receives the `onCallEnd` event.\n\nFor a group call, when a user calls the `AgoraChatCallManager.releaseRTC` method to leave a call, other users in the call receive the `onUserLeaved` event.\n\n## Next steps\n\n### Turn on or off the speaker\n\nYou can call the `AgoraChatCallManager.speakerOn` or `AgoraChatCallManager.speakerOff` method to turn on or turn off the speaker during a call. \n\n```\nawait AgoraChatCallManager.speakerOn();\nawait AgoraChatCallManager.speakerOff();\n```\n\n### Mute or unmute the microphone\n\nYou can call the `AgoraChatCallManager.mute` or `AgoraChatCallManager.unMute` method to mute or unmute the microphone during a call. When the microphone status changes, the peer user in the one-to-one call or other users in the group call receive the `AgoraChatCallKitEventHandler.onUserMuteAudio` event.\n\n```\nawait AgoraChatCallManager.mute();\nawait AgoraChatCallManager.unMute();\n```\n\n### Turn on or off the camera\n\nYou can call the `AgoraChatCallManager.cameraOn` or `AgoraChatCallManager.cameraOff` method to turn on or turn off the camera. The peer user in the one-to-one call or other users in the group call receive the `AgoraChatCallKitEventHandler.onUserMuteVideo` event.\n\n```\nawait AgoraChatCallManager.cameraOn();\nawait AgoraChatCallManager.cameraOff();\n```\n\n### Switch the camera\n\nYou can call the `AgoraChatCallManager.switchCamera` method to switch the front and rear cameras.\n\n```\nawait AgoraChatCallManager.switchCamera();\n```\n\n### Get the local preview view  \n\nWhen making a one-to-one video call or group call, you can call the `AgoraChatCallManager.getLocalVideoView` method to obtain the local camera preview widget.\n\n```\nWidget? localPreviewWidget = AgoraChatCallManager.getLocalVideoView();\n```\n\n### Get the remote video view\n\nDuring a one-to-one video call or group call, if a user joins the call, you can call the `AgoraChatCallManager.getRemoteVideoView` method to obtain the video widget of this user.\n\n```\n// agoraUid: The Agora RTF user ID of a user in the call.\nWidget? remoteVideoWidget = AgoraChatCallManager.getRemoteVideoView(agoraUid);\n```\n\n### Delete the listener handler\n\nYou can call the `AgoraChatCallManager.removeEventListener` method to remove callbacks when the callkit is no longer needed.\n\n```\n// UNIQUE_HANDLER_ID: The key that was set when you added AgoraChatCallKitEventHandler.\nAgoraChatCallManager.removeEventListener(UNIQUE_HANDLER_ID);\n```\n\n## Push notifications\n\nIn scenarios where the app runs on the background or goes offline, use push notifications to ensure that the callee receives the call invitation. To enable push notifications, see [Set up push notifications](https://docs.agora.io/en/agora-chat/develop/offline-push?platform=flutter).\n\nOnce push notifications are enabled, when a call invitation arrives, a notification message pops out on the notification panel. Users can click the message to view the call invitation.\n\n## Reference\n\n### API list\n\nThis section provides other reference information that you can refer to when implementing real-time audio and video communications functionalities.\n\nIn `agora_chat_callkit`, `AgoraChatCallManager` provides the following APIs:\n\n|  Method          | Description              |\n| :-------------------------- | :------------------ |\n| addEventListener          | Adds an event listener.   |\n| removeEventListener       | Removes an event listener.   |\n| initRTC             | Initializes the Agora RTC.         |\n| startSingleCall           | Makes a one-to-one call.    |\n| startInviteUsers       | Invites users to join a group call.      |\n| answer          | Answers a call.        |\n| releaseRTC       | Rejects a call or hangs up a call.      |\n| speakerOn           | Turns on the speaker.            |\n| speakerOff       | Turns off the speaker.          |\n| mute          | Mutes the microphone.            |\n| unMute        | Unmutes the microphone.          |\n| cameraOn           | Turns on the camera.           |\n| cameraOff     | Turns off the camera.        |\n| switchCamera   | Switches the front and rear cameras.           |\n| getLocalVideoView  | Gets the local video view.            |\n| getRemoteVideoView    | Gets the remote video view.          |\n\n`AgoraChatCallKitEventHandler` contains call-related events. For details, see [Listen for callback events](#Listen for callback events).\n\n### Sample project\n\nIf demo is required, configure the following information in the `example/lib/config.dart` file:\n\n```\nclass Config {\n  static String agoraAppId = \"\";\n  static String appkey = \"\";\n\n  static String appServerDomain = \"\";\n\n  static String appServerRegister = '';\n  static String appServerGetAgoraToken = '';\n\n  static String appServerTokenURL = \"\";\n  static String appServerUserMapperURL = \"\";\n}\n```\n\nTo obtain the Agora RTC token, you need to set up an [App Server](./authentication#Deploy an app server to generate tokens) and provide a mapping service for the agora user ID and the Agora Chat user ID.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feasemob%2Fflutter_chat_callkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feasemob%2Fflutter_chat_callkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feasemob%2Fflutter_chat_callkit/lists"}