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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 15:55:18