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

Flutter:应用后台/终止状态下如何正确配置Pusher Beams?

问题描述

已完成Firebase控制台、Pusher Beams的注册与配置,复制flutter pusher_beams包示例项目后,实现了前台接收通知弹窗、后台通知显示在通知栏的功能,但遇到以下问题:

  • 应用处于后台时点击通知,无法识别点击的通知内容,调用PusherBeams.instance.getInitialMessage()返回null,添加延迟也无效果,断点未触发;
  • 应用被杀死后不知如何处理通知:使用flutter_background_service时,设置isForegroundMode: true会出现无法隐藏的通知;设为false时,应用被杀死后服务也会停止;workmanager最快15分钟调用一次,无法满足实时需求。

现有代码

class _MyHomePageState extends State<MyHomePage> {
  @override
  initState() {
    super.initState();

    initPusherBeams();
  }

  initPusherBeams() async {
    // 查看当前订阅的兴趣组
    print(await PusherBeams.instance.getDeviceInterests());

    // Web端不适用
    if (!kIsWeb) {
      await PusherBeams.instance
          .onInterestChanges((interests) => {
            print('Interests: $interests')});

      await PusherBeams.instance
          .onMessageReceivedInTheForeground(_onMessageReceivedInTheForeground);
    }
    await _checkForInitialMessage();
  }

  Future<void> _checkForInitialMessage() async {
    await Future.delayed(const Duration(seconds: 1));
    final initialMessage = await PusherBeams.instance.getInitialMessage();
    if (initialMessage != null) {
      _showAlert('Initial Message Is:', initialMessage.toString());
    }
  }

  void _onMessageReceivedInTheForeground(Map<Object?, Object?> data) {
    _showAlert(data["title"].toString(), data["body"].toString());
  }

  void _showAlert(String title, String message) {
    AlertDialog alert = AlertDialog(
        title: Text(title), content: Text(message), actions: const []);

    showDialog(
      context: context,
      builder: (BuildContext context) {
        return alert;
      },
    );
  }
}
解决方案

问题1:后台点击通知无法获取内容

核心原因

getInitialMessage()仅针对应用完全被杀死后点击通知启动的场景,后台状态下点击通知不属于该范畴,需通过后台消息回调处理。

修复步骤

  1. 注册后台消息回调:在initPusherBeams()中添加onMessageReceivedInTheBackground监听(注意该方法需为全局静态函数,避免上下文绑定问题)。
  2. 处理后台点击逻辑:在回调中获取通知内容,结合全局状态管理或NavigatorKey实现UI更新或页面跳转。

修改后的代码示例:

// 全局静态函数,处理后台消息
Future<void> _onMessageReceivedInTheBackground(Map<Object?, Object?> data) async {
  print('后台收到通知:$data');
  // 将通知数据存入全局变量,供应用前台后处理
  GlobalNotificationData.notificationData = data;
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    initPusherBeams();
    // 监听应用从后台回到前台的事件
    WidgetsBinding.instance.addObserver(AppLifecycleObserver());
  }

  initPusherBeams() async {
    print(await PusherBeams.instance.getDeviceInterests());

    if (!kIsWeb) {
      await PusherBeams.instance.onInterestChanges((interests) => {
            print('Interests: $interests')
          });

      await PusherBeams.instance
          .onMessageReceivedInTheForeground(_onMessageReceivedInTheForeground);
      // 注册后台消息回调
      await PusherBeams.instance
          .onMessageReceivedInTheBackground(_onMessageReceivedInTheBackground);
    }
    await _checkForInitialMessage();
  }

  // 保留原有_showAlert、_checkForInitialMessage等方法

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(AppLifecycleObserver());
    super.dispose();
  }
}

// 应用生命周期监听类
class AppLifecycleObserver extends WidgetsBindingObserver {
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      // 应用回到前台,检查未处理的通知数据
      if (GlobalNotificationData.notificationData != null) {
        final context = GlobalNavigator.navigatorKey.currentContext;
        if (context != null) {
          showDialog(
            context: context,
            builder: (ctx) => AlertDialog(
              title: Text(GlobalNotificationData.notificationData!["title"].toString()),
              content: Text(GlobalNotificationData.notificationData!["body"].toString()),
            ),
          );
        }
        // 处理完清空数据
        GlobalNotificationData.notificationData = null;
      }
    }
  }
}

// 全局存储类
class GlobalNotificationData {
  static Map<Object?, Object?>? notificationData;
}

// 全局NavigatorKey,需在MaterialApp中配置
class GlobalNavigator {
  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
}

同时在MaterialApp中配置navigatorKey:

MaterialApp(
  navigatorKey: GlobalNavigator.navigatorKey,
  // 其他配置项
)

问题2:应用被杀死后处理通知

核心原因

  • flutter_background_service前台模式必须显示通知是Android API 26+的强制要求;后台模式下应用被杀死后服务会被系统回收。
  • workmanager的15分钟限制是Android系统对后台任务的管控,无法突破。

解决方案

利用Pusher Beams本身的推送唤醒机制,结合轻量级后台回调处理:

  1. Android端配置:
    在AndroidManifest.xml中添加权限:

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    

    确保Pusher Beams的PusherBeamsMessagingService已在清单中注册(通常包会自动配置)。

  2. Flutter端处理:
    扩展onMessageReceivedInTheBackground回调,应用被杀死后收到通知时,系统会唤醒该回调执行轻量级任务(如更新本地数据、触发本地通知)。

示例代码:

Future<void> _onMessageReceivedInTheBackground(Map<Object?, Object?> data) async {
  print('应用杀死后收到通知:$data');
  
  // 示例:更新本地数据库
  await LocalDatabase.saveNotification(data);
  
  // 示例:触发本地通知
  final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
  await flutterLocalNotificationsPlugin.initialize(
    InitializationSettings(
      android: AndroidInitializationSettings('@mipmap/ic_launcher'),
    ),
  );
  
  await flutterLocalNotificationsPlugin.show(
    DateTime.now().millisecondsSinceEpoch,
    data["title"].toString(),
    data["body"].toString(),
    NotificationDetails(
      android: AndroidNotificationDetails(
        'pusher_beams_channel',
        'Pusher Beams通知',
        importance: Importance.max,
        priority: Priority.high,
      ),
    ),
  );
}

注意:系统唤醒的回调执行时间有限(通常几秒),仅适合处理轻量级任务,无法执行耗时操作。若需复杂逻辑,可考虑在回调中启动临时isolate处理,但需注意系统资源限制。


内容的提问来源于stack exchange,提问作者Jakub Rony Roncak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 01:35:22