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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:55:21