CallKeep는 실시간 통신 앱에서 원활하고 시스템 수준의 전화 처리를 가능하게 하는 Flutter 플러그인입니다. 이는 iOS CallKit과 Android ConnectionService를 통합하여, 앱이 네이티브 UI 요소로 들어오는 전화를 표시하고, 통화 상태를 관리하며, 앱이 백그라운드에 있을 때도 올바른 통화 기능을 유지할 수 있도록 합니다. 라이브 스트리밍 및 실시간 통신 사용 사례를 위해 설계되었으며, 사용자가 장치의 네이티브 통화 인터페이스를 통해 전화를 받을 수 있도록 하여 신뢰성과 사용성을 향상시킵니다. 이 플러그인은 Android와 iOS 플랫폼을 모두 지원하며, Flutter WebRTC 커뮤니티에서 유지 보수되고 있습니다.
사용 사례
실시간 음성 및 비디오 통화
네이티브 호출 인터페이스 통합
백그라운드 통화 관리
라이브 스트리밍 애플리케이션
메시징 앱에서의 통화 처리
주요 기능
iOS CallKit 통합
Android ConnectionService 지원
시스템 수준 호출 UI
백그라운드 호출 지속성
크로스 플랫폼 호환성
적합한 대상
VoIP 애플리케이션
원격의료 플랫폼
팀 커뮤니케이션 도구
고객 지원 시스템
실시간 협업 앱
고려 사항
플랫폼별 구성이 필요합니다
양 플랫폼에서 적절한 권한이 필요합니다
푸시 알림을 위해 추가 설정이 필요할 수 있습니다
콜 기반 기능 외에는 적합하지 않습니다
하위 WebRTC 구현에 따라 다릅니다
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
callkeep
iOS CallKit and Android ConnectionService for Flutter
Support FCM and PushKit
Keep in mind Callkit is banned in China, so if you want your app in the chinese AppStore consider include a basic alternative for notifying calls (ex. FCM notifications with sound).
* P-C-M means -> presenter / controller / manager
Introduction
Callkeep acts as an intermediate between your call system (RTC, VOIP...) and the user, offering a native calling interface for handling your app calls.
This allows you (for example) to answer calls when your device is locked even if your app is terminated.
Initial setup
Basic configuration. In Android a popup is displayed before starting requesting some permissions to work properly.
callKeep.setup(
showAlertDialog: () async {
finalBuildContext context = navigatorKey.currentContext!;
returnawait showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
returnAlertDialog(
title: constText('Permissions Required'),
content: constText(
'This application needs to access your phone accounts'),
actions: <Widget>[
TextButton(
child: constText('Cancel'),
onPressed: () => Navigator.of(context).pop(false),
),
TextButton(
child: constText('OK'),
onPressed: () => Navigator.of(context).pop(true),
),
],
);
},
) ??
false;
},
options:<String, dynamic>{
'ios': {
'appName': 'CallKeepDemo',
},
'android': {
'additionalPermissions': [
'android.permission.CALL_PHONE',
'android.permission.READ_PHONE_NUMBERS'
],
// Required to get audio in background when using Android 11'foregroundService': {
'channelId': 'com.company.my',
'channelName': 'Foreground service for my app',
'notificationTitle': 'My app is running on background',
'notificationIcon': 'mipmap/ic_notification_launcher',
},
},
});
This configuration should be defined when your application wakes up, but keep in mind this alert will appear if you aren't granting the needed permissions yet.
A clean alternative is to control by yourself the required permissions when your application wakes up, and only invoke the setup() method if those permissions are granted.
Events
Callkeep offers some events to handle native actions during a call.
These events are quite crucial because they act as an intermediate between the native calling UI and your call P-C-M.
What does it mean?
Assuming your application already implements some calling system (RTC, Voip, or whatever) with its own calling UI, you are using some basic controls:
Future<void> answerCall(CallKeepPerformAnswerCallAction event) async {
print('CallKeepPerformAnswerCallAction ${event.callUUID}');
// notify to your call P-C-M the answer action
};
Future<void> endCall(CallKeepPerformEndCallAction event) async {
print('CallKeepPerformEndCallAction ${event.callUUID}');
// notify to your call P-C-M the end action
};
Future<void> didPerformSetMutedCallAction(CallKeepDidPerformSetMutedCallAction event) async {
print('CallKeepDidPerformSetMutedCallAction ${event.callUUID}');
// notify to your call P-C-M the muted switch action
};
Future<void> didToggleHoldCallAction(CallKeepDidToggleHoldAction event) async {
print('CallKeepDidToggleHoldAction ${event.callUUID}');
// notify to your call P-C-M the hold switch action
};
Display incoming calls in foreground, background or terminate state
The incoming call concept we are looking for is firing an incoming call action when "something" is received in our app.
I've tested this concept with FCM and it works pretty fine.
finalFlutterCallkeep _callKeep = FlutterCallkeep();
bool _callKeepStarted = false;
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
awaitFirebase.initializeApp();
if (!_callKeepStarted) {
try {
await _callKeep.setup(callSetup);
_callKeepStarted = true;
} catch (e) {
print(e);
}
}
// then process your remote message looking for some call uuid// and display any incoming call
}
Displaying incoming calls is really simple if you are receiving FCM messages (or whatever). This example shows how to show and close any incoming call:
Notice that getting data from the payload can be done as you want, this is an example.
showAlertDialog: () async {
finalBuildContext context = navigatorKey.currentContext!;
returnawait showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
returnAlertDialog(
title: constText('Permissions Required'),
content: constText(
'This application needs to access your phone accounts'),
actions: <Widget>[
TextButton(
child: constText('Cancel'),
onPressed: () => Navigator.of(context).pop(false),
),
TextButton(
child: constText('OK'),
onPressed: () => Navigator.of(context).pop(true),
),
],
);
},
) ??
false;
},
```
### FAQ
> I don't receive the incoming call
Receiving incoming calls depends on FCM push messages (or the system you use) for handling the call information and displaying it.
Remember FCM push messages not always works due to data-only messages are classified as "low priority". Devices can throttle and ignore these messages if your application is in the background, terminated, or a variety of other conditions such as low battery or currently high CPU usage. To help improve delivery, you can bump the priority of messages. Note; this does still not guarantee delivery. More info [here](https://firebase.flutter.dev/docs/messaging/usage/#low-priority-messages)
> How can I manage the call if the app is terminated and the device is locked?
Even in this scenario, the `backToForeground()` method will open the app and your call P-C-M will be able to work.
## push test tool
Please refer to the [Push Toolkit](/tools/) to test callkeep offline push.