Flutter Web中点击Firebase通知跳转指定页面的实现问题
解决Flutter Web Firebase通知点击无响应及路由跳转问题
问题分析
你的notificationclick事件未触发,核心原因是事件处理缺少异步操作保障,且未完成应用聚焦/打开、与Flutter前端的参数传递逻辑;同时Flutter端未监听通知点击相关事件,导致无法触发路由跳转。
步骤1:修改Service Worker文件(firebase-messaging-sw.js)
调整事件监听逻辑,确保点击通知后能聚焦/打开应用,并传递跳转参数:
importScripts("https://www.gstatic.com/firebasejs/9.15.0/firebase-app-compat.js"); importScripts("https://www.gstatic.com/firebasejs/9.15.0/firebase-messaging-compat.js"); firebase.initializeApp({ apiKey: "...", authDomain: "...", databaseURL: "...", projectId: "...", storageBucket: "...", messagingSenderId: "...", appId: "...", measurementId: "..." }); const messaging = firebase.messaging(); messaging.onBackgroundMessage(function(message) { console.log('Received background message ', message); const content = JSON.parse(message.data.content); const notificationTitle = content.payload.summary ? content.payload.summary : content.payload.title; const notificationOptions = { body: content.payload.body, data: message.data, // 携带完整通知数据 }; self.registration.showNotification(notificationTitle, notificationOptions); }); // 处理通知点击事件 self.addEventListener('notificationclick', function(event) { console.log('Notification clicked:', event); // 关闭通知 event.notification.close(); // 用waitUntil确保异步操作完成 event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }) .then(function(clientList) { // 查找已打开的应用标签页 for (let client of clientList) { // 匹配应用根路径(根据你的实际URL调整) if (client.url.includes(location.origin) && 'focus' in client) { // 发送跳转数据给Flutter前端 client.postMessage({ type: 'NOTIFICATION_CLICK', payload: JSON.parse(event.notification.data.content).payload }); return client.focus(); } } // 无打开标签页时,携带参数打开新页面 const payload = JSON.parse(event.notification.data.content).payload; if (clients.openWindow) { return clients.openWindow(`${location.origin}/?redirectTo=${payload.redirectTo}&id=${payload.id}`); } }) ); });
步骤2:Flutter Web端处理通知跳转
在Flutter代码中监听通知相关事件,实现路由跳转:
1. 初始化Firebase并监听核心事件
import 'dart:convert'; import 'dart:html' as html; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; // 全局导航Key,用于无上下文时跳转路由 final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: const FirebaseOptions( apiKey: "...", authDomain: "...", databaseURL: "...", projectId: "...", storageBucket: "...", messagingSenderId: "...", appId: "...", measurementId: "...", ), ); // 处理应用从后台被通知唤醒的场景 FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { final content = jsonDecode(message.data['content']); _handleNotificationRedirect(content['payload']); }); // 处理前台收到通知的场景(可选,如需点击前台通知跳转) FirebaseMessaging.onMessage.listen((RemoteMessage message) { final content = jsonDecode(message.data['content']); // 可配合flutter_local_notifications显示前台通知,点击时调用跳转逻辑 }); // 监听Service Worker发送的消息(应用已打开时点击通知) if (kIsWeb) { html.window.addEventListener('message', (event) { final data = (event as html.MessageEvent).data; if (data['type'] == 'NOTIFICATION_CLICK') { _handleNotificationRedirect(data['payload']); } }); // 处理应用未打开时,通过通知URL携带的参数跳转 final uri = Uri.parse(html.window.location.href); final redirectTo = uri.queryParameters['redirectTo']; final id = uri.queryParameters['id']; if (redirectTo == 'contractDetails' && id != null) { WidgetsBinding.instance.addPostFrameCallback((_) { navigatorKey.currentState?.pushNamed( '/contractDetails', arguments: id, ); }); } } runApp(const MyApp()); } // 统一处理通知跳转逻辑 void _handleNotificationRedirect(Map<String, dynamic> payload) { if (payload['redirectTo'] == 'contractDetails') { navigatorKey.currentState?.pushNamed( '/contractDetails', arguments: payload['id'], ); } } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( navigatorKey: navigatorKey, initialRoute: '/', routes: { '/': (context) => const HomePage(), '/contractDetails': (context) => ContractDetailsPage( id: ModalRoute.of(context)?.settings.arguments as String, ), }, ); } } // 示例页面 class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold(appBar: AppBar(title: const Text('首页'))); } } class ContractDetailsPage extends StatelessWidget { final String id; const ContractDetailsPage({super.key, required this.id}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('合同详情')), body: Center(child: Text('合同ID:$id')), ); } }
关键注意事项
- 确保
firebase-messaging-sw.js文件放在Flutter Web项目的web根目录下,且Firebase配置与Flutter端完全一致。 - 测试时需使用HTTPS环境(localhost默认支持,生产环境必须HTTPS),否则Service Worker无法正常工作。
- 发送通知的
data字段需保持格式正确,确保redirectTo和id参数能被正确解析。
内容的提问来源于stack exchange,提问作者SeriousMonk
相关产品推荐
相关产品推荐

