在每个屏幕上的 fcm 通知上显示小吃栏

Posted

技术标签:

【中文标题】在每个屏幕上的 fcm 通知上显示小吃栏【英文标题】:show snackbar on fcm notification on every screen 【发布时间】:2020-07-14 21:02:32 【问题描述】:

我想在应用通知到达时显示小吃栏。 但是当我在第一个屏幕上配置 firebase 时,小吃栏仅在用户在该屏幕上时显示。 我尝试创建一个类来获取 BuildContext 并基于它显示snackbar,但不起作用并且不显示snackbar。

这是我的 HomeScreen.dart:

class _HomeScreenState extends State<HomeScreen> 
  @override
  void initState() 
    super.initState();
    Future.delayed(Duration.zero, () 
      NotificationManger.init(context: context);
      Fcm.initConfigure();
    );
  

  @override
  Widget build(BuildContext context) 
    return StoreConnector<AppState, Store<AppState>>(
      converter: (store) => store,
      onInit: (store) => initApp(store),
      builder: (context, store) 
        return BlocProvider<HomeBloc>(
          create: (context) 
            return HomeBloc(homeRepository: homeRepository)..add(ScreenOpened());
          ,
          child: BlocListener<HomeBloc, HomeState>(
            listener: (context, state) async ,
            child: BlocBuilder<HomeBloc, HomeState>(
              builder: (context, state) 
                return Scaffold(
                  key: _scaffoldKey,
                  ...
                );
              ,
            ),
          ),
        );
      ,
    );
  

这是我的 Fcm.dart

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) 
  if (message.containsKey('data')) 
    final dynamic data = message['data'];
  
  if (message.containsKey('notification')) 
    final dynamic notification = message['notification'];
  


class Fcm 
  static final FirebaseRepository repository = FirebaseRepository();
  static final FirebaseMessaging _fcm = FirebaseMessaging();

  static initConfigure() 
    if (Platform.isios) _iosPermission();

    _fcm.requestNotificationPermissions();
    _fcm.autoInitEnabled();

    _fcm.configure(
      onMessage: (Map<String, dynamic> message) async => NotificationManger.onMessage(message),
      onLaunch: (Map<String, dynamic> message) async => NotificationManger.onLaunch(message),
      onResume: (Map<String, dynamic> message) async => NotificationManger.onResume(message),
      onBackgroundMessage: myBackgroundMessageHandler,
    );

    _fcm.getToken().then((String token) 
      print('token: $token');
      repository.setUserNotifToken(token);
    );
  

  static _iosPermission() 
    _fcm.requestNotificationPermissions(IosNotificationSettings(sound: true, badge: true, alert: true));
    _fcm.onIosSettingsRegistered.listen((IosNotificationSettings settings) 
      print("Settings registered: $settings");
    );
  

这是我的 NotificationManager.dart:

class NotificationManger 
  static BuildContext _context;

  static init(@required BuildContext context) 
    _context = context;
  

  static onMessage(Map<String, dynamic> message) 
    print(message);
    _showSnackbar(data: message);
  

  static onLaunch(Map<String, dynamic> message) 
    print(message);
  

  static onResume(Map<String, dynamic> message) 
    print(message);
  

  static _showSnackbar(@required Map<String, dynamic> data) 
    // showDialog(context: _context, builder: (_) => );
    SnackBar snackBar = SnackBar(
      content: Text(
        data['data']['title'],
        style: TextStyle(
          fontFamily: 'Vazir',
          fontSize: 16.0,
        ),
      ),
      backgroundColor: ColorPalette.primary,
      behavior: SnackBarBehavior.floating,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(45.0),
      ),
      elevation: 3.0,
    );
    Scaffold.of(_context).showSnackBar(snackBar);
  

main.dart

class App extends StatelessWidget 
  final Store<AppState> store;
  App(this.store);
  @override
  Widget build(BuildContext context) 
    return StoreProvider(
      store: store,
      child: MaterialApp(
        ...
      ),
    );
  

我正在使用 redux 和 bloc,因此任何使用这些工具的方法对我来说都可以。

这是我的示例屏幕:


class Reminders extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: appBar,
      body: Center(
        child: Text('reminders'),
      ),
    );
  

解决方案: 将NotificationManger.init(globalKey: _scaffoldKey);添加到所有屏幕即可解决问题。

class Reminders extends StatelessWidget 
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) 
    NotificationManger.init(globalKey: _scaffoldKey);

    return Scaffold(
      key: _scaffoldKey,
      appBar: appBar,
      body: Center(
        child: Text('reminders'),
      ),
    );
  


解决方案 2

使用Get 库只使用一个功能,无需在所有屏幕中添加它:https://pub.dev/packages/get

【问题讨论】:

【参考方案1】:

问题在于为 NotificationManager 小部件注册脚手架,因为每次将新脚手架添加到新屏幕的堆栈中时,您都需要在 NotificationManager 中注册该屏幕的脚手架。这是因为行:

Scaffold.of(_context).showSnackBar(snackBar);

在 NoticicationManager 中只会查找小部件树,直到它找到第一个脚手架并在那里调用它。由于您在 HomeScreen 小部件中调用 NotificationManger.init(context: context); 并传递 HomeScreen 的上下文,因此它将仅存在于该脚手架内。因此,如果您从 HomeScreen 导航到具有不同脚手架的新小部件,它将不会有 NotificationManager 作为子级。

要解决此问题,请确保在为应用加载的第一页中调用 Fcm.initConfigure();,对于您导航到的任何页面,请在有状态小部件的 initState() 方法中调用 NotificationManger.init(context: context); 以注册当前脚手架或者如果它们是无状态小部件,则可以在返回脚手架之前将其添加到 build 方法中。

【讨论】:

我已经更新了我的答案,提供了关于如何操作的建议,看看它是否有效,如果您还有其他问题,请尝试添加您的 main.dart 或明确您是如何离开 HomeScreen 的跨度> 您是否尝试将Fcm.initConfigure(); 移动到您的main.dart,因为它是构建方法中的一个无状态小部件,并将NotificationManger.init(context: context); 添加到您的每个页面的initState 方法中? 我想在用户登录时配置 fcm。所以我的 homeScreen 是用户登录时的根屏幕。并且我的 homeScreen 的构建方法在导航时运行。 好的,那么该页面可以保持不变,您能告诉我您如何导航到不同页面的示例以及该页面的代码吗? 让我们continue this discussion in chat。【参考方案2】:

在所有屏幕上复制相同的代码可能并不理想。如果你必须改变某些东西,你将不得不改变一切,否则你会看到不一致的地方。 对于小吃店,您可以使用这个库并在 1 行代码中解决您的问题:

关于你的 onMessage 方法:

onMessage(message)
Get.snackbar("message", message); // this line

是的,无论用户在哪个屏幕上,都会显示snackbar,你不需要脚手架的上下文,也不需要任何上下文,只需调用库方法就可以了。

库:https://pub.dev/packages/get

【讨论】:

以上是关于在每个屏幕上的 fcm 通知上显示小吃栏的主要内容,如果未能解决你的问题,请参考以下文章

当用户在聊天屏幕上时,如何使 FCM 通知不显示通知消息?

在 Android Q 的锁定屏幕上收到 fcm 通知后开始活动

FCM 推送通知在返回应用程序时打开,而不是在 iOS 的状态栏中显示

小吃店与吐司

Android如何在屏幕上显示通知

当用户点击通知 FCM 时如何移动到特定屏幕 iOS