You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter CallKit Incoming在iOS应用终止状态下无法工作的问题

iOS后台/终止状态CallKit无法触发及通话通知隐藏方案

问题1:iOS后台/终止状态无法显示CallKit

核心原因

iOS终止状态下,普通FCM数据消息无法唤醒Flutter引擎,_firebaseMessagingBackgroundHandler不会执行。必须依赖**VoIP推送(PushKit)**触发原生层的CallKit调用——PushKit是iOS专门为语音/视频通话设计的推送机制,能在App终止/后台状态下直接唤醒原生代码。

修复步骤

  1. 完善PushKit原生代理实现
    在AppDelegate.swift中确保正确实现PushKit的代理方法,直接在原生层触发CallKit,无需经过Flutter后台handler:

    import PushKit
    
    class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate {
        override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            // 初始化PushKit
            let registry = PKPushRegistry(queue: DispatchQueue.main)
            registry.delegate = self
            registry.desiredPushTypes = [.voIP]
            
            // 其他Flutter初始化代码
            GeneratedPluginRegistrant.register(with: self)
            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
        
        // 上传VoIP令牌到服务器
        func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
            let token = pushCredentials.token.map { String(format: "%02x", $0) }.joined()
            // 将token发送到你的后端服务
        }
        
        // 接收VoIP推送并触发CallKit
        func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
            let data = payload.dictionaryPayload["data"] as? [String: Any] ?? [:]
            
            // 构造CallKit参数并调用原生方法
            FlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming([
                "id": UUID().uuidString,
                "nameCaller": "APP Screen Sharing Call",
                "handle": data["senderName"] as? String ?? "",
                "type": 0,
                "avatar": data["senderImageUrl"] as? String ?? "",
                "extra": [
                    "guestRoomCode": data["guestRoomCode"] as? String ?? "",
                    "receiverName": data["receiverName"] as? String ?? ""
                ],
                "ios": [
                    "supportsVideo": true,
                    "ringtonePath": "system_ringtone_default"
                ]
            ])
            completion()
        }
    }
    

    同时在Info.plist添加VoIP权限描述:

    <key>NSExtensionUsageDescription</key>
    <string>需要VoIP权限接收屏幕共享来电通知</string>
    
  2. 切换为VoIP推送类型
    服务器必须发送VoIP格式的推送,而非普通FCM通知。示例Payload:

    {
        "aps": {
            "content-available": 1
        },
        "data": {
            "topic": "screen.sharing.invite",
            "senderName": "测试用户",
            "senderImageUrl": "https://example.com/avatar.jpg",
            "guestRoomCode": "123456"
        }
    }
    
  3. 移除Flutter后台handler的无效调用
    iOS终止状态下Flutter引擎无法启动,_firebaseMessagingBackgroundHandler中的showCallkitIncoming不会执行,可保留该监听处理其他非通话类后台消息,但通话触发逻辑仅保留原生PushKit代理中的实现。


问题2:隐藏该通话的通知,保留其他通知正常显示

iOS端

  • 使用VoIP推送触发CallKit时,系统会直接显示系统级来电界面,不会生成普通通知,无需额外处理。
  • 若需隐藏未接来电通知,可在CallKitParams的missedCallNotification中设置showNotification: false。

Android端

  • 利用独立通知渠道隔离:你已为通话设置了专属渠道screen_share_call,可动态禁用该渠道来隐藏通话通知,不影响其他渠道:
    import 'package:flutter_local_notifications/flutter_local_notifications.dart';
    
    void toggleCallNotificationChannel(bool enable) async {
      final FlutterLocalNotificationsPlugin notificationsPlugin =
          FlutterLocalNotificationsPlugin();
      if (enable) {
        await notificationsPlugin.enableNotificationChannel('screen_share_call');
      } else {
        await notificationsPlugin.disableNotificationChannel('screen_share_call');
      }
    }
    
  • 若需来电时仅在锁屏显示、不弹出通知,可调整AndroidParams参数:
    android: const AndroidParams(
      isShowLogo: true,
      textColor: "#ffffff",
      actionColor: '#dda088',
      backgroundColor: '#dda088',
      isShowCallID: true,
      isCustomNotification: true,
      isShowFullLockedScreen: true,
      // 设置静音无震动,减少干扰
      ringtonePath: '',
      incomingCallNotificationChannelName: 'screen_share_call',
      missedCallNotificationChannelName: "screen_share_miss_call",
    )
    
    注:Android前台服务要求必须显示通知,无法完全隐藏,仅能通过样式调整降低干扰。

内容的提问来源于stack exchange,提问作者amit.flutter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 07:32:03