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

基于Firebase Messaging实现Flutter应用角标更新问题求助

将flutter_app_badger集成到Firebase Messaging通知处理的方案

问题背景

我正在使用flutter_app_badger和Firebase Messaging搭建通知系统,期望收到新通知时更新应用角标数字,开发环境为FlutterFlow。目前已完成两项工作:

  • 编写了应用前台运行时更新角标的独立函数
  • 配置了由应用内动作触发的通知系统
    但无法将两者结合,现提供push_notification_handler代码,寻求集成FlutterAppBadger.updateBadgeCount()的方案。

现有代码

import 'dart:async';
import 'dart:convert';
import 'serialization_util.dart';
import '../backend.dart';
import '../../flutter_flow/flutter_flow_theme.dart';
import '../../flutter_flow/flutter_flow_util.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import '../../index.dart';
import '../../main.dart';

final _handledMessageIds = <String?>{};

class PushNotificationsHandler extends StatefulWidget {
  const PushNotificationsHandler({Key? key, required this.child})
      : super(key: key);

  final Widget child;

  @override
  _PushNotificationsHandlerState createState() =>
      _PushNotificationsHandlerState();
}

class _PushNotificationsHandlerState extends State<PushNotificationsHandler> {
  bool _loading = false;

  Future handleOpenedPushNotification() async {
    if (isWeb) {
      return;
    }

    final notification = await FirebaseMessaging.instance.getInitialMessage();
    if (notification != null) {
      await _handlePushNotification(notification);
    }
    FirebaseMessaging.onMessageOpenedApp.listen(_handlePushNotification);
  }

  Future _handlePushNotification(RemoteMessage message) async {
    if (_handledMessageIds.contains(message.messageId)) {
      return;
    }
    _handledMessageIds.add(message.messageId);

    if (mounted) {
      setState(() => _loading = true);
    }
    try {
      final initialPageName = message.data['initialPageName'] as String;
      final initialParameterData = getInitialParameterData(message.data);
      final pageBuilder = pageBuilderMap[initialPageName];
      if (pageBuilder != null) {
        final page = await pageBuilder(initialParameterData);
        await Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => page),
        );
      }
    } catch (e) {
      print('Error: $e');
    } finally {
      if (mounted) {
        setState(() => _loading = false);
      }
    }
  }

  @override
  void initState() {
    super.initState();
    handleOpenedPushNotification();
  }

  @override
  Widget build(BuildContext context) => _loading
      ? Container(
          color: FlutterFlowTheme.of(context).primaryBtnText,
          child: Image.asset(
            'assets/images/Screen_Shot_2022-11-22_at_12.59.57.png',
            fit: BoxFit.contain,
          ),
        )
      : widget.child;
}

final pageBuilderMap = <String, Future<Widget> Function(Map<String, dynamic>)>{
  'SignInSignUpPage': (data) async => SignInSignUpPageWidget(),
  'ForgotPasswordPage': (data) async => ForgotPasswordPageWidget(),
  'ContactsListPage': (data) async => ContactsListPageWidget(
        tabToOpen: getParameter(data, 'tabToOpen'),
      ),
  'SyncProcessPage': (data) async => SyncProcessPageWidget(),
  'SyncConfirmationPage': (data) async => SyncConfirmationPageWidget(),
  'ContactDetailsPage': (data) async => ContactDetailsPageWidget(
        idReference: getParameter(data, 'idReference'),
        comment: getParameter(data, 'comment'),
      ),
  'ContactDeletePage': (data) async => ContactDeletePageWidget(
        idReference: getParameter(data, 'idReference'),
        comment: getParameter(data, 'comment'),
      ),
  'ContactInvitationPage': (data) async => ContactInvitationPageWidget(
        idReference: getParameter(data, 'idReference'),
      ),
  'AddConfirmationAfterInvitePage': (data) async =>
      AddConfirmationAfterInvitePageWidget(
        uID: getParameter(data, 'uID'),
        phraseState: getParameter(data, 'phraseState'),
      ),
  'AddContactsOptionPage': (data) async => AddContactsOptionPageWidget(),
  'SearchNewContactsPage': (data) async => SearchNewContactsPageWidget(),
  'NewContactInvitationConfirmationPage': (data) async =>
      NewContactInvitationConfirmationPageWidget(),
  'NewContactScanPage': (data) async => NewContactScanPageWidget(),
  'AddConfirmationAfterScanPageNew': (data) async =>
      AddConfirmationAfterScanPageNewWidget(
        uID: getParameter(data, 'uID'),
        phraseState: getParameter(data, 'phraseState'),
      ),
  'AddConfirmationAfterScanPageOld': (data) async =>
      AddConfirmationAfterScanPageOldWidget(
        uID: getParameter(data, 'uID'),
        phraseState: getParameter(data, 'phraseState'),
      ),
  'ShareProfileOptionsPage': (data) async => ShareProfileOptionsPageWidget(),
  'InAppQRcodeSharePage': (data) async => InAppQRcodeSharePageWidget(),
  'WebQRcodeSharePage': (data) async => WebQRcodeSharePageWidget(),
  'ProfileEditPage': (data) async => ProfileEditPageWidget(),
  'GameSettingsPage': (data) async => GameSettingsPageWidget(),
  'GamePage': (data) async => GamePageWidget(),
  'TutorialPage': (data) async => TutorialPageWidget(),
  'AboutPage': (data) async => AboutPageWidget(),
  'FAQPage': (data) async => FAQPageWidget(),
  'PrivacyPolicyPage': (data) async => PrivacyPolicyPageWidget(),
};

bool hasMatchingParameters(Map<String, dynamic> data, Set<String> params) =>
    params.any((param) => getParameter(data, param) != null);

Map<String, dynamic> getInitialParameterData(Map<String, dynamic> data) {
  try {
    final parameterDataStr = data['parameterData'];
    if (parameterDataStr == null ||
        parameterDataStr is! String ||
        parameterDataStr.isEmpty) {
      return {};
    }
    return jsonDecode(parameterDataStr) as Map<String, dynamic>;
  } catch (e) {
    print('Error parsing parameter data: $e');
    return {};
  }
}

集成步骤

1. 导入flutter_app_badger包

在代码顶部添加导入语句:

import 'package:flutter_app_badger/flutter_app_badger.dart';

2. 添加角标更新方法

在_PushNotificationsHandlerState类中添加角标更新的核心方法,支持获取当前角标数并累加:

Future<void> _updateBadgeCount() async {
  int currentCount = 0;
  // 尝试获取当前角标数,兼容部分不支持获取的设备
  try {
    currentCount = await FlutterAppBadger.getBadgeCount() ?? 0;
  } catch (e) {
    print('获取当前角标数失败: $e');
  }
  // 每次收到通知角标数+1
  await FlutterAppBadger.updateBadgeCount(currentCount + 1);
}

3. 监听前台通知并更新角标

修改initState方法,添加前台通知的监听逻辑,确保应用在前台时收到通知立即更新角标:

@override
void initState() {
  super.initState();
  handleOpenedPushNotification();
  // 监听前台收到的推送通知
  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    _updateBadgeCount();
  });
}

4. 处理通知打开后的角标更新

当用户点击通知进入应用时,可选择将角标数减1或清空,修改_handlePushNotification的finally块:

finally {
  if (mounted) {
    setState(() => _loading = false);
  }
  // 用户打开通知后更新角标
  int currentCount = await FlutterAppBadger.getBadgeCount() ?? 0;
  if (currentCount > 0) {
    await FlutterAppBadger.updateBadgeCount(currentCount - 1);
  } else {
    await FlutterAppBadger.removeBadge();
  }
}

5. 处理应用未启动时的角标更新

在handleOpenedPushNotification中,处理应用未启动时点击通知的场景,同步更新角标:

Future handleOpenedPushNotification() async {
  if (isWeb) {
    return;
  }

  final notification = await FirebaseMessaging.instance.getInitialMessage();
  if (notification != null) {
    await _handlePushNotification(notification);
    // 应用未启动时点击通知,更新角标
    int currentCount = await FlutterAppBadger.getBadgeCount() ?? 0;
    if (currentCount > 0) {
      await FlutterAppBadger.updateBadgeCount(currentCount - 1);
    } else {
      await FlutterAppBadger.removeBadge();
    }
  }
  FirebaseMessaging.onMessageOpenedApp.listen(_handlePushNotification);
}

注意事项

  • 平台兼容性:flutter_app_badger支持iOS和部分Android机型,iOS需在Xcode中开启Push Notifications和Background Modes下的Remote notifications权限;Android部分机型可能需要额外配置。
  • 如果推送消息中携带了badge字段,也可以直接使用该字段的值来设置角标,替换累加逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:40:40