如何解决Flutter iOS端点击Firebase推送通知后启动页闪退问题?
根据你描述的情况——Flutter iOS能正常接收FCM推送,但点击后启动页显示随即崩溃,Android端完全正常,已经设置了异步main并调整过FCM代码位置,还没抓到异常日志——我给你几个针对性的排查和修复步骤,应该能解决问题:
1. 确保原生AppDelegate的推送回调在主线程执行
iOS的UI操作必须在主线程完成,哪怕Flutter侧做了处理,原生层的推送回调如果跑在非主线程,很容易触发崩溃。打开ios/Runner/AppDelegate.swift(如果是Objective-C项目就找对应的.m文件),检查并修改推送点击的处理方法:
Swift版本:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // 强制把所有操作放到主线程 DispatchQueue.main.async { // 如果需要和Flutter通信,在这里调用MethodChannel if let flutterVC = window?.rootViewController as? FlutterViewController { let channel = FlutterMethodChannel(name: "fcm_notification_channel", binaryMessenger: flutterVC.binaryMessenger) channel.invokeMethod("onNotificationTap", arguments: response.notification.request.content.userInfo) } // 一定要调用completionHandler,否则系统会认为处理超时 completionHandler() } }
Objective-C版本:
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { dispatch_async(dispatch_get_main_queue(), ^{ FlutterViewController *flutterVC = (FlutterViewController *)self.window.rootViewController; FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"fcm_notification_channel" binaryMessenger:flutterVC]; [channel invokeMethod:@"onNotificationTap" arguments:response.notification.request.content.userInfo]; completionHandler(); }); }
如果之前你没显式实现这个方法,FCM的默认处理可能在非主线程触发UI操作,这是iOS端推送崩溃的常见原因。
2. 调整异步main的初始化顺序
虽然你已经用了异步main,但如果FCM初始化和Flutter引擎启动的顺序不对,点击推送时可能引擎还没完全准备好。试试调整main函数的结构:
void main() async { // 先确保Flutter绑定初始化完成,这是异步操作的前提 WidgetsFlutterBinding.ensureInitialized(); // 初始化Firebase await Firebase.initializeApp(); // 注册推送点击回调,放在引擎初始化之后 FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { // 这里处理跳转逻辑,比如导航到指定页面 // 注意:如果是冷启动,要确保页面路由已经初始化完成 print("Notification tapped: ${message.data}"); }); // 最后启动App runApp(const MyApp()); }
如果还是有问题,可以把推送回调的注册延迟到UI树构建完成后:
runApp(const MyApp()); WidgetsBinding.instance.addPostFrameCallback((_) { FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { // 处理跳转逻辑 }); });
3. 排查iOS配置的潜在问题
打开ios/Runner/Info.plist,检查两个关键配置:
- 确认
UIBackgroundModes包含remote-notification,保证App能在后台接收推送:
<key>UIBackgroundModes</key> <array> <string>remote-notification</string> </array>
- 如果推送里包含HTTP链接,检查
NSAppTransportSecurity设置,确保没有ATS限制导致崩溃:
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
(如果不想全局开启,可以添加指定域名的例外,更安全)
4. 强制捕获崩溃日志
你说没检测到异常,大概率是日志没抓到。试试这两种方法:
- Xcode控制台捕获:用Xcode直接运行App到iPhone 7 Plus,然后点击推送触发崩溃,Xcode的控制台会输出完整的崩溃堆栈,能直接看到崩溃的原因。
- Flutter全局异常捕获:在main函数里添加全局异常监听,捕获Flutter侧的未处理异常:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // Flutter框架异常捕获 FlutterError.onError = (FlutterErrorDetails details) { FlutterError.presentError(details); print("Flutter error: ${details.exception}"); print("Stack trace: ${details.stack}"); }; // 异步任务异常捕获 runZonedGuarded(() async { await Firebase.initializeApp(); runApp(const MyApp()); }, (error, stackTrace) { print("Uncaught async error: $error"); print("Stack trace: $stackTrace"); }); }
另外,在Xcode的Scheme设置里添加--verbose参数(Product > Scheme > Edit Scheme > Run > Arguments),运行时会输出更详细的Flutter日志,帮你定位问题。
5. 测试极简推送,排除数据解析问题
有时候推送的自定义数据格式不正确,Flutter侧解析时会崩溃。先发送一个不带任何自定义data的纯文本推送,点击看看是否还崩溃。如果不崩溃,再逐步添加自定义数据,排查是哪个字段导致的解析错误。
内容的提问来源于stack exchange,提问作者S. M. Asif

