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

Flutter引擎在iOS通知服务扩展中MethodChannel无法回传数据

问题:iOS通知服务扩展中Flutter引擎无法回传数据

已将Flutter模块编译为框架并添加到Xcode的通知服务扩展目标中,目前数据可成功发送至Flutter端,但始终无法接收返回值——无论是用return还是invokeMethod都无效。在纯iOS App的AppDelegate中调用该模块时功能正常,但在通知扩展里只能成功运行引擎、发送数据到Flutter,收不到返回结果。怀疑是通知扩展的生命周期特性(非完整App,仅为回调)导致,或是引擎/通道注册存在遗漏。


iOS端代码(NotificationService.swift)

import Flutter
import UserNotifications


let flutterEngine = FlutterEngine(name: "The engine")
let flutterDataChannel = FlutterMethodChannel(name: "com.test.shouldwork/ios", binaryMessenger: flutterEngine.binaryMessenger)

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?
    
    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        
        if let bestAttemptContent = bestAttemptContent {
            // 初始化引擎
             flutterEngine.run()
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            struct Content:Codable{
                var title:String
                    var body:String
                
            }
                
                let content = Content(title:bestAttemptContent.title,body: bestAttemptContent.body)
                var data:Data
                do{
                    data = try JSONEncoder().encode(content)
                    let convertedString = String(data: data, encoding: .utf8)!
                    flutterDataChannel.invokeMethod("dataToFlutter", arguments: convertedString, result:{(result) -> () in
                        var request = URLRequest(url: URL(string: "https://us-central1-wellenbrecher-3c570.cloudfunctions.net/debug?data=isback")!)
                        let task = URLSession.shared.dataTask(with: request)
                        task.resume()
                      
                        let result = result as! NSDictionary
                        //bestAttemptContent.title = result["title"] as! String
                        //bestAttemptContent.body = result["body"] as! String$
                        bestAttemptContent.title = "was mutated !"
                        contentHandler(bestAttemptContent)
                        
                        
                    })
               
                }
                catch{
                   print("error")
                }
             }

        }
        
        
    }
    
    override func serviceExtensionTimeWillExpire() {
        // 扩展即将被系统终止前调用,需返回修改后的内容或原 payload
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}

Dart端代码(main.dart)

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;


final platform = const MethodChannel('com.test.shouldwork/ios');

notification_extension(data){
  data['title']= "Hello world !";
  return data;
}


Future<void> _receiveFromNative(MethodCall call) async {
   try {
     if (call.method == "dataToFlutter") {
       final String data = call.arguments;
       http.get(Uri.parse('https://us-central1-wellenbrecher-3c570.cloudfunctions.net/debug?data=$data'));
      
       var jData = await jsonDecode(data);
       var jResult = notification_extension(jData);
      
       return jResult;
     }
   } on PlatformException catch (error) {
     print(error);
   }
}


main() async {
    WidgetsFlutterBinding.ensureInitialized();
    http.get(Uri.parse('https://us-central1-wellenbrecher-3c570.cloudfunctions.net/debug?flutter=true'));
  
    try{
      platform.setMethodCallHandler(_receiveFromNative);
    }
    catch (e){
      http.get(Uri.parse('https://us-central1-wellenbrecher-3c570.cloudfunctions.net/debug?isReady_error=$e'));
    }
}

解决方案

1. 修正Dart方法的返回类型

你的Dart端_receiveFromNative方法声明为Future<void>,但要返回数据给Native端,必须改为返回Future<dynamic>(或具体类型),否则Native端无法接收返回值:

Future<dynamic> _receiveFromNative(MethodCall call) async {
   try {
     if (call.method == "dataToFlutter") {
       final String data = call.arguments;
       http.get(Uri.parse('https://us-central1-wellenbrecher-3c570.cloudfunctions.net/debug?data=$data'));
      
       var jData = await jsonDecode(data);
       var jResult = notification_extension(jData);
      
       return jResult;
     }
     return null; // 处理未匹配的方法调用
   } on PlatformException catch (error) {
     print(error);
     return error; // 将异常回传给Native端
   }
}

2. 替换不可靠的延迟等待,监听引擎初始化完成

用固定延迟等待引擎初始化不可靠,改为监听引擎启动完成事件,确保通道注册成功后再调用方法:

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    self.contentHandler = contentHandler
    bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
    
    guard let bestAttemptContent = bestAttemptContent else {
        contentHandler(request.content)
        return
    }

    // 监听引擎初始化完成事件
    flutterEngine.run(withEntrypoint: nil) { [weak self] result in
        guard let self = self, result == .success else {
            contentHandler(bestAttemptContent)
            return
        }
        
        struct Content: Codable {
            var title: String
            var body: String
        }
        
        let content = Content(title: bestAttemptContent.title, body: bestAttemptContent.body)
        do {
            let data = try JSONEncoder().encode(content)
            guard let convertedString = String(data: data, encoding: .utf8) else {
                contentHandler(bestAttemptContent)
                return
            }
            
            self.flutterDataChannel.invokeMethod("dataToFlutter", arguments: convertedString) { result in
                // 安全处理返回值,避免强制类型转换崩溃
                if let resultDict = result as? [String: Any], let newTitle = resultDict["title"] as? String {
                    bestAttemptContent.title = newTitle
                } else {
                    bestAttemptContent.title = "was mutated !"
                }
                contentHandler(bestAttemptContent)
            }
        } catch {
            print(error)
            contentHandler(bestAttemptContent)
        }
    }
}

3. 适配通知扩展的生命周期限制

通知扩展的运行时间被系统严格限制(通常仅几秒),需优化流程:

  • 移除调试用的网络请求,减少耗时
  • 用Release模式编译Flutter框架,加快引擎初始化速度
  • 在serviceExtensionTimeWillExpire方法中兜底返回内容,避免系统强制终止扩展

内容的提问来源于stack exchange,提问作者aloys-c

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 12:25:30