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

Flutter Firebase Messaging:应用终止时的数据消息处理问题

Flutter FCM数据消息在应用终止状态下点击通知无法获取数据的解决方案

背景

我使用Flutter开发聊天应用,采用Firebase Cloud Messaging (FCM)发送数据消息(而非通知消息)。

服务器端发送逻辑

服务器发送消息的结构如下:

const message = {
    token: deviceToken,
    data: {
        senderName: senderName,
        message: messageContent,
        conversationId: conversationId,
        companyId: companyId,
        isGroup: 'false'
    }
}

客户端已实现的处理逻辑

1. 后台消息处理器

Future<void> backgroundHandler(RemoteMessage message) async {
    // Handle background message
    LocalNotificationService.display(message);
}

2. 应用初始化入口

Future<void> main() async {
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp();
    FirebaseMessaging.onBackgroundMessage(backgroundHandler);
    runApp(MyApp());
}

3. 根组件初始化逻辑

void initState() {
    super.initState();
    print("Inside Init");

    // Initialize local notification service
    LocalNotificationService.initialize();

    // Listen for messages when the app is in the foreground
    FirebaseMessaging.onMessage.listen((message) {
      print('Foreground message received: ${message.messageId}');
      if (message.data.isNotEmpty) {
        print('Displaying Message');
        LocalNotificationService.display(message);
      }
    });

    // Check for initial message when the app is launched
    handleInitialMessage();
  }

  Future<void> handleInitialMessage() async {
    RemoteMessage? initialMessage =
        await FirebaseMessaging.instance.getInitialMessage();
    if (initialMessage != null) {
      Fluttertoast.showToast(
          msg: 'Inside Handle get initial message ${initialMessage.data}');
      if (initialMessage.data.isNotEmpty) {
        handleRemoteMessage(initialMessage);
      } else if (initialMessage.notification != null) {
        Fluttertoast.showToast(msg: 'Notification caused app launch');
      } else {
        Fluttertoast.showToast(msg: 'No data in initial message');
      }
    } else {
      Fluttertoast.showToast(msg: 'No initial message received');
    }
  }

  void handleRemoteMessage(RemoteMessage message) {
    // Extract and handle the message data
    Map<String, dynamic> data = message.data;
    final String companyId = data['companyId'];
    final String conversationId = data['conversationId'];
    final bool isGroup = data['isGroup'] == 'true';

    User? currentUser = FirebaseAuth.instance.currentUser;
    if (currentUser != null) {
      String email = currentUser.email ?? '';
      String employeeEmail = email.split('-')[1];

      fetchEmployeeIdFromFirestore(
        incomingCompanyId: companyId,
        employeeEmail: employeeEmail,
      ).then((fetchedCompanyId) {
        if (fetchedCompanyId != null) {
          Navigator.pushNamed(
            navigatorKey.currentState!.context,
            '/ChatScreen',
            arguments: {
              'companyId': companyId,
              'employeeId': fetchedCompanyId,
              'conversationId': conversationId,
              'isGroup': isGroup,
            },
          );
        } else {
          print('Error: Employee email not found');
        }
      });
    } else {
      Navigator.pushNamed(navigatorKey.currentState!.context, '/LoginScreen');
    }
  }

问题

应用处于**终止(已杀死)**状态时,用户点击通知后,message.data为空,无法触发目标页面导航。使用Firebase控制台发送测试通知时,同样得到空的{} Map。

解决方案

1. 修改本地通知展示逻辑,附加数据到payload

核心问题:后台处理数据消息时,本地通知未携带FCM的data字段,导致点击通知启动应用时无法获取原始数据。修改LocalNotificationService.display方法:

import 'dart:convert';

static Future<void> display(RemoteMessage message) async {
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
      'your_channel_id',
      'your_channel_name',
      importance: Importance.max,
      priority: Priority.high,
    );
    var platformChannelSpecifics = NotificationDetails(
      android: androidPlatformChannelSpecifics,
    );

    // 将FCM数据转为JSON字符串存入通知payload
    String payload = jsonEncode(message.data);

    await flutterLocalNotificationsPlugin.show(
      0,
      message.data['senderName'],
      message.data['message'],
      platformChannelSpecifics,
      payload: payload, // 关键:绑定数据到通知
    );
}

2. 监听本地通知点击事件,解析数据并导航

在LocalNotificationService.initialize()中添加点击监听,处理应用从终止状态启动的场景:

import 'dart:convert';

static Future<void> initialize() async {
    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中的FCM数据
        Map<String, dynamic> data = jsonDecode(payload);
        // 构建RemoteMessage对象或直接调用导航逻辑
        RemoteMessage message = RemoteMessage(data: data);
        // 执行导航处理
        handleRemoteMessage(message);
      }
    });
}

注意:handleRemoteMessage需保证全局可访问,或通过导航键确保上下文有效。

3. 确认Android配置正确性

在AndroidManifest.xml中确保Firebase消息服务配置正确:

<service
    android:name=".MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

若自定义了FirebaseMessagingService,需确保onMessageReceived方法正确转发数据消息到本地通知。

4. 验证服务器发送格式

确保服务器仅发送纯数据消息,不混合notification字段。FCM中,纯数据消息在后台/终止状态下会触发onBackgroundMessage;若同时包含notification字段,Android系统可能直接展示通知,跳过onBackgroundMessage处理。

关键原理

应用终止状态下,FCM数据消息会触发onBackgroundMessage,此时需通过本地通知将数据传递给用户。点击本地通知时,getInitialMessage()不一定能获取完整数据(纯数据消息场景下),因此必须通过本地通知的payload手动保存并传递数据,在点击时解析并执行导航逻辑。

内容的提问来源于stack exchange,提问作者Guru Subramani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 11:51:00