Flutter应用终止时,如何通过Firebase通知跳转至指定页面?
问题描述
收到通知时需跳转至新页面而非主屏幕,当前代码在应用前台/后台状态下可正常工作,但应用完全终止时点击通知无法触发跳转。
现有代码:
flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: (String? payload) async { try { if (payload != null && payload.isNotEmpty) { Navigator.push( context, MaterialPageRoute( builder: (context) => NewScreen(info: payload.toString()))); } else {} } catch (e) {} return; }); void sendPushMessage(String token, String body, String title) async { try { await http.post( Uri.parse('https://fcm.googleapis.com/fcm/send'), headers: <String, String>{ 'Content-Type': 'application/json', 'Authorization': 'key=thekey' }, body: jsonEncode(<String, dynamic>{ 'priority': 'high', 'data': <String, dynamic>{ 'click_action': 'FLUTTER_NOTIFICATION_CLICK', 'status': 'done', 'body': body, 'title': title }, "notification": <String, dynamic>{ "title": title, "body": body, "android_channel_id": "androidchannelid" }, "to": token, }), ); } catch (e) { if (kDebugMode) { print('error push notifications'); } } }
解决方案
应用终止时Flutter尚未初始化,onSelectNotification回调不会触发,需通过原生层+Flutter初始化后处理的方式解决:
1. 调整FCM消息结构
改用纯Data消息(去掉notification字段),并在data中添加路由标识和页面参数,确保系统将消息直接传递给应用而非接管展示:
void sendPushMessage(String token, String body, String title) async { try { await http.post( Uri.parse('https://fcm.googleapis.com/fcm/send'), headers: <String, String>{ 'Content-Type': 'application/json', 'Authorization': 'key=thekey' }, body: jsonEncode(<String, dynamic>{ 'priority': 'high', 'data': <String, dynamic>{ 'click_action': 'FLUTTER_NOTIFICATION_CLICK', 'status': 'done', 'body': body, 'title': title, 'route': '/new-screen', // 新增路由标识 'payload': 'your-page-info' // 新增页面所需参数 }, "to": token, }), ); } catch (e) { if (kDebugMode) { print('error push notifications'); } } }
2. Flutter端全局配置
(1)添加全局NavigatorKey
用于在初始化阶段获取导航上下文:
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( navigatorKey: navigatorKey, home: HomeScreen(), ); } }
(2)在main函数中处理启动参数
捕获应用终止时点击通知的启动请求,初始化完成后执行跳转:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // 初始化通知插件 final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); final InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid); await flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: (String? payload) async { if (payload != null && payload.isNotEmpty) { Navigator.push( navigatorKey.currentContext!, MaterialPageRoute(builder: (context) => NewScreen(info: payload)) ); } }); // 处理Android端应用终止时的启动参数 final RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { final String? route = initialMessage.data['route']; final String? payload = initialMessage.data['payload']; if (route == '/new-screen' && payload != null) { WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.push( navigatorKey.currentContext!, MaterialPageRoute(builder: (context) => NewScreen(info: payload)) ); }); } } runApp(MyApp()); }
3. iOS端原生适配
修改AppDelegate.swift,处理应用终止时的通知点击参数,并通过MethodChannel传递给Flutter:
import UIKit import Flutter import FirebaseMessaging @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { FirebaseApp.configure() GeneratedPluginRegistrant.register(with: self) // 处理应用终止时的通知启动参数 if let remoteNotification = launchOptions?[.remoteNotification] as? [String: Any] { let data = remoteNotification["data"] as? [String: Any] let route = data?["route"] as? String let payload = data?["payload"] as? String let controller = window?.rootViewController as! FlutterViewController let channel = FlutterMethodChannel(name: "notification_route", binaryMessenger: controller.binaryMessenger) channel.invokeMethod("navigateToRoute", arguments: ["route": route, "payload": payload]) } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
Flutter端监听iOS的MethodChannel请求
在main函数中添加监听:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // ... 之前的初始化代码 ... // 监听iOS端的路由跳转请求 const MethodChannel channel = MethodChannel('notification_route'); channel.setMethodCallHandler((call) async { if (call.method == 'navigateToRoute') { final args = call.arguments as Map<String, dynamic>; final String? route = args['route']; final String? payload = args['payload']; if (route == '/new-screen' && payload != null) { WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.push( navigatorKey.currentContext!, MaterialPageRoute(builder: (context) => NewScreen(info: payload)) ); }); } } }); runApp(MyApp()); }
内容的提问来源于stack exchange,提问作者Ini
相关产品推荐
相关产品推荐

