【问题标题】:Opening a popup in a flutter Flutter when onBackgroundMessage gets executed当 onBackgroundMessage 被执行时,在颤振颤振中打开一个弹出窗口
【发布时间】:2020-01-23 19:44:28
【问题描述】:

我已经实现了flutter onBackgroundMessage,当设备接收到Firebase Cloud Messaging数据消息时触发;我应该打开一个弹出窗口,但在这个事件处理程序中我没有context 对象。实现这一目标的正确方法是什么?

【问题讨论】:

  • 我在我的主页的 initState 中创建了监听器。因此,当它执行时,我的构建方法将被调用,因此上下文将存在..它对我有用。你在哪里打电话给你的听众?
  • 监听器在我的firebase云消息对象的configure方法中定义;默认情况下,它不接收上下文。另外,据我了解,处理程序必须是 staticglobal 对象,其签名是: static Future myBackgroundMessageHandler( Map message) async {...} 所以我不太明白如何传递上下文。另外,由于我们正在谈论后台消息,难道不是我有某种方式可以“启动”应用程序吗?显然已经停止了?
  • 如何为这些后台通知创建一个 BLOC 并从 Widget 订阅?当有来自 BLOC 的新事件时打开弹出窗口。

标签: flutter firebase-cloud-messaging


【解决方案1】:

我用静态方法创建了一个类:

class FirebaseMessagingHandler {
  final FirebaseMessaging firebaseMessaging = FirebaseMessaging();
  final _bloc = AppModule.to.getBloc<FirebaseMessagingHandlerBloc>();

  void setListeners() {
    if (Platform.isIOS) _iOSPermission();

    getToken();

    refreshToken();
  }

  void getToken() {
    firebaseMessaging.getToken().then((token) {
      _bloc.saveToken(token);
      print('DeviceToken = $token');
    });
  }

  void _iOSPermission() {
    firebaseMessaging.configure();
    firebaseMessaging.requestNotificationPermissions(IosNotificationSettings(sound: true, badge: true, alert: true));
    firebaseMessaging.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
    });
  }

  void refreshToken() {
    firebaseMessaging.onTokenRefresh.listen((token) {
      _bloc.refreshToken(token);
    });
  }

  void showDialog(BuildContext context, Map<String, dynamic> message) {
      // data
  }

  void showErrorDialog(BuildContext context, dynamic error) {
      // data
  }

  void redirectToPage(BuildContext context, Map<String, dynamic> message) {
    // data
  }
}

在我的主页(打开我的应用程序时总是会调用的页面)我调用配置:

class _HomePageState extends State<HomePage> {
  final _fcm = FirebaseMessagingHandler();

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

  void firebaseCloudMessagingListeners() {
    _fcm.firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        try {
          _fcm.showDialog(context, message);
        } catch (e) {
          _fcm.showErrorDialog(context, e);
        }
      },
      onLaunch: (Map<String, dynamic> message) async {
        try {
          _fcm.redirectToPage(context, message);
        } catch (e) {
          _fcm.showErrorDialog(context, e);
        }
      },
      onResume: (Map<String, dynamic> message) async {
        try {
          _fcm.redirectToPage(context, message);
        } catch (e) {
          _fcm.showErrorDialog(context, e);
        }
      },
    );
  }
}

【讨论】:

  • 谢谢,这个解决方案正是我想要的,易于实施。
【解决方案2】:

如果您想在应用程序中显示弹出窗口,则不需要onBackgroundMessage - 仅用于在后台收到消息时处理数据。收到消息时无法启动应用程序。

但是,如果用户点击通知,应用就会启动,并且会调用 onResume 或 onLaunch 回调。

当发生这种情况时,您可以通知相关屏幕显示一个弹出窗口。

这是一个简单的实现:

firebase_notification_receiver.dart:

import 'dart:async';
import 'package:firebase_messaging/firebase_messaging.dart';

class NotificationEvent {
  final Map<String, dynamic> content;

  /// whether the notification was delivered while the app was in the foreground
  final bool inApp;

  NotificationEvent({this.content, this.inApp = false});
}

class FirebaseNotificationReceiver extends NotificationReceiver {

  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();

  StreamController<NotificationEvent> _controller = StreamController<NotificationEvent>.broadcast();

  StreamSubscription _streamSubscription;

  Function(NotificationEvent) _listener;

  init{

    // add the rest of the code to initialise firebase here

    _firebaseMessaging.configure(

      /// Fires when App was in foreground when receiving the notification
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        _controller.sink.add(NotificationEvent(content: message, inApp: true));
      },

      /// Fires when App was in background when receiving the notification and user has tapped on it
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        _controller.sink.add(NotificationEvent(content: message));
      }

      /// Fires when App was closed when receiving the notification and user has tapped on it
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        _controller.sink.add(NotificationEvent(content: message));
      },
    );
    _streamSubscription =
            _controller.stream.listen(_onStreamEvent, onError: (e) {
      print("Notification Stream error $e");
    });
  }

  setListener(Function(NotificationEvent) onData) {
    this._listener = onData;
  }
}

main.dart:

    // imports go here

    void main(){
       final notificationReceiver = NotificationReceiver.firebase();

      runApp(
        MultiProvider(
          providers: [
            Provider<NotificationReceiver>(
                builder: (_) => notificationReceiver),
            // more providers go here
          ],
          child: App(), // Your custom app class
        ),
      );

    }

notification_listenable.dart:


import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class NotificationListenable extends StatefulWidget {

  final Widget child;
  final Function(NotificationEvent) onData;

  const NotificationListenable({@required this.child, this.onData});

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

class _NotificationListenableState extends State<NotificationListenable> {

  @override
  Widget build(BuildContext context) {
    Provider.of<NotificationReceiver>(context).setListener(widget.onData);
    return widget.child;
  }
}

在 my_screen.dart 中:


/// add your imports here

class MyScreen extends StatefulWidget {

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

class MyScreenState extends State<MyScreen> {

  final _scaffoldKey = GlobalKey<ScaffoldState>();

  void _onNotification(NotificationEvent n) {
    (_scaffoldKey.currentState)?.showSnackBar(
       SnackBar(
         duration: Duration(seconds: 2),
         content: Text("I am a pop up"),
       ),
    ),
 }

 @override
 Widget build(BuildContext context) {

  return NotificationListenable(
    child: YourCustomScreenContent(),
    onData: _onNotification,
  );
}

【讨论】:

  • 嘿,你回答的游戏改变元素是当我在后台收到消息时我无法打开应用程序,所以我只需要“存储”事件并在应用程序时呈现给用户开始(或再次进入前台)。这是真的 ?另外,我喜欢您处理通知的侦听器的方法。谢谢!
  • 基本上我需要实现以下目标:用户收到带有问题的通知;我希望用户有机会点击是/否,仅此而已。就像 WhatsApp 通知一样,这使您有机会在不打开应用程序的情况下快速回复。有了这个,如果可以覆盖标准通知,也许我可以用它们实现这一点;否则我需要数据消息,这需要明确的逻辑。但是,关于后台通知,我的问题是:因为我需要在应用程序关闭时对此进行测试,我该如何调试呢?
  • 啊 - 你的意思是丰富的通知?听起来您需要编写这些通知 - 在本机代码中,即在 iOS 的 Swift / ObjC 或 Android 的 Java / Kotlin 中。我认为没有办法通过 dart/flutter 小部件来做到这一点,除非有第三方库可以做到这一点。
猜你喜欢
  • 2022-08-22
  • 2021-04-05
  • 2020-04-24
  • 2019-06-26
  • 2021-05-16
  • 1970-01-01
  • 2021-05-13
  • 2021-06-11
  • 1970-01-01
相关资源
最近更新 更多