如何在点击推送通知时打开特定屏幕

siv*_*mar 21 android ios dart flutter

我试图在点击推送通知时实现打开特定屏幕,我的有效负载如下所示:

 var payload = {
        notification: {
            title: notificationTitle,
            body: notificationMessage,
            click_action:"/screena",sound:"default",
        }
    };
Run Code Online (Sandbox Code Playgroud)

我收到通知但我无法捕捉到flutter中的通知点击事件如何捕获它.我正在使用扑动消息

https://github.com/flutter/plugins/tree/master/packages/firebase_messaging

和我的firebase推送消息服务代码看起来像这样

 pushMessagingService() async{
messagingreference.configure(
onMessage: (Map<String, dynamic> message) {

  print("I am here in on message");
  print(message);
},
onLaunch: (Map<String, dynamic> message) {
  print("I am here onLaunch");
  print(message);
},
onResume: (Map<String, dynamic> message) {
  print("I am hereonResume");
  print(message);
},
);
  messagingreference.requestNotificationPermissions(
  const IosNotificationSettings(sound: true, badge: true, alert: true));
 messagingreference.onIosSettingsRegistered
  .listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
 });
 messagingreference.getToken().then((String token) async {


print(token);
 });
 }
Run Code Online (Sandbox Code Playgroud)

这里我可以得到消息,因为@xqwzts在我的应用程序处于前台时在消息中说,但我的问题是如何从系统托盘中引发的推送通知中捕获点击事件并导航到所需的屏幕.

xqw*_*zts 28

这里有几件事:

1- click_action必须设置为"FLUTTER_NOTIFICATION_CLICK"

click_action必须在data有效载荷的部分中设置2-

DATA='{
  "notification": {
    "body": "this is a body",
    "title": "this is a title"
  },
  "data": {
    "click_action": "FLUTTER_NOTIFICATION_CLICK",
    "sound": "default", 
    "status": "done",
    "screen": "screenA",
  },
  "to": "<FCM TOKEN>"
}'
Run Code Online (Sandbox Code Playgroud)

这应该允许您onMessage在flutter应用程序的处理程序中接收消息.

从那里你可以打电话Navigator.of(context).pushNamed(message['screen']).

如果您还没有BuildContext,那么您可以注册一个GlobalKey作为navigatorKey您的财产MaterialApp,并使用它来访问您的Navigator全球通过GlobalKey.currentState

  • 不为我工作。这是我在 onMessage 中收到的消息 `{notification: {title: First Notification, body: hELLO hELLO Test}, data: {click_action: FLUTTER_NOTIFICATION_CLICK}}` 但重定向不起作用。我已经用 `Navigator.of(navigatorKey.currentContext).pushNamed('test');` 配置了 onMessage 和 onResume。navigatorKey 是 GlobalKey 设置 MaterialApp 及其 navigatorKey。 (5认同)
  • 在这里,当我的应用程序处于前台时,我可以收到您在消息中所说的消息,但我的问题是如何从系统托盘中引发的推送通知中捕获点击事件并导航到所需的屏幕 (4认同)
  • 我能够捕获消息 ['screen'] 并且调用 Navigator 没有打开路由 (2认同)
  • 我对 Dart 还很陌生,最后一段让我很困惑——你能进一步解释一下吗@xqwzts? (2认同)
  • 如果您对如何实现 navigatorKey 感到困惑,请参阅此答案:/sf/answers/3737808651/ (2认同)

小智 13

要提供有效载荷,请使用:

1- click_action 必须设置为 "FLUTTER_NOTIFICATION_CLICK" - 在 android manifest 文件中,我们已经定义了它的意图。

2- click_action 必须在有效载荷的数据部分中设置

为 NavigatorState 定义一个全局变量:

import 'package:flutter/cupertino.dart';

/// Global variables
/// * [GlobalKey<NavigatorState>]
class GlobalVariable {
  
  /// This global key is used in material app for navigation through firebase notifications.
  /// [navState] usage can be found in [notification_notifier.dart] file.
  static final GlobalKey<NavigatorState> navState = GlobalKey<NavigatorState>();
}

Run Code Online (Sandbox Code Playgroud)

转到您的 MaterialApp 并添加

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return ScrollConfiguration(
      behavior: BounceScrollBehavior(),
      child: MaterialApp(
        navigatorKey: GlobalVariable.navState,
        debugShowCheckedModeBanner: false,
        theme: themeData,
        home: App(),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

当您按下应用栏中的通知时,它会调用 onResume。您可以按如下方式导航到所需页面。

void listenToNotification() {
    fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        getPreviousNotifications();
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: ${message["data"]}");
        SchedulerBinding.instance.addPostFrameCallback((_) {
          Navigator.of(GlobalVariable.navState.currentContext)
              .push(MaterialPageRoute(
                  builder: (context) => TimelineView(
                        campaignId: message["data"]["campaign"],
                      )));
        });
      },
    );
  }
Run Code Online (Sandbox Code Playgroud)


X S*_*ham 11

对于任何想在 Null Safety(适用于 iOS 和 Android)之前迁移到最新版本的 Firebase Messaging 的人,以下是步骤

pubspec.yaml

firebase_core: ^0.7.0
firebase_messaging: ^8.0.0-dev.15
Run Code Online (Sandbox Code Playgroud)

main.dart

GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

Future<void> main() async {
      
        WidgetsFlutterBinding.ensureInitialized();
        await Firebase.initializeApp();
        await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
          alert: true,
          badge: true,
          sound: true,
        );
        runApp(new MyApp());
}


class MyApp extends StatefulWidget {

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

class _MyAppState extends State<MyApp> {

@override
  Widget build(BuildContext context) {
  return MaterialApp(
              navigatorKey: navigatorKey,
              title: ...

   );
  }

}
Run Code Online (Sandbox Code Playgroud)

主屏幕.dart

    Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
      print("onBackgroundMessage: $message");
    }
    
    class HomeScreen extends StatefulWidget {
      @override
      _HomeScreenState createState() => _HomeScreenState();
    }
    
    class _HomeScreenState extends State<HomeScreen>{
    
      @override
      void initState() {
        super.initState();
    
    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
    FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
        print("onMessage: $message");
    });
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
        print("onMessageOpenedApp: $message");
            
                 
          if (message.data["navigation"] == "/your_route") {
            int _yourId = int.tryParse(message.data["id"]) ?? 0;
            Navigator.push(
                    navigatorKey.currentState.context,
                    MaterialPageRoute(
                        builder: (context) => YourScreen(
                              yourId:_yourId,
                            )));
        });
      }
    }
Run Code Online (Sandbox Code Playgroud)

注意到 iOS 通知将出现在顶部(平视显示器)和触发方法onMessage(当应用程序在前台时)和onBackgroundMessage(当应用程序在后台或终止时)。

对于 android 通知将出现在托盘的顶部和触发方法onBackgroundMessage(当应用程序在后台或终止时)仅。您必须使用第三方解决方案,例如flutter_local_notificationsoverlay_supportonMessage(前台应用程序)期间显示通知。

对于 iOS,当点击通知时(当应用在后台、终止或前台时),方法onMessageOpenedApp将被触发。对于 Android,此方案仅在应用程序在后台运行或终止时才有效(如果启用了抬头通知显示/横幅 - 您需要为 android 创建 channel_id)

您不再需要发送click_action: FLUTTER_CLICK_ACTION数据负载才能在通知中获得可点击事件。FirebaseMessaging 将为您处理

  • 如果类型通知是数据,则不会调用 onMessageOpenedApp,知道吗? (2认同)

Dha*_*ara 10

第 1 步:将 firebase 通知中的一对键值对作为click_action:传递FLUTTER_CLICK_ACTION

第 2 步:使用第 1 步,您将收到通知内部onResumeonLaunch方法的 onTap 回调。

步骤 3:执行以下场景以在单击通知时导航到特定屏幕。

  • 当您构建 MaterialApp 时,传递一个navigatorKey参数,该参数指定用于导航器的键,然后将该键分配给您的材料应用程序,如下所示:
class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey(debugLabel: "Main Navigator");

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      navigatorKey: navigatorKey,
      home: new Scaffold(
        appBar: AppBar(),
        body: new Container(),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)
  • 现在,从onResumeoronLaunch方法使用以下代码行导航到您的屏幕:
 navigatorKey.currentState.push(
    MaterialPageRoute(builder: (_) => Dashboard())
 );
Run Code Online (Sandbox Code Playgroud)


Raj*_*Jr. 9

由于@xqwzts方法非常适合在应用处于打开状态时接收消息,

以下示例将导航到特定页面,

[该代码仅来自FIREBASE消息处理插件示例代码,并且通过FIREBASE控制台发送数据时它会导航到指定页面]

//eg:if you give /Nexpage3  in status field then it will navigate to Nextpage3 of your App
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

了解这2件事,FCM通知有2节

Firebase云消息传递页面中的“第一个消息标题”部分称为“ 通知数据” [当应用程序最小化或关闭时,它将显示为通知]

网页底部的第二个消息标题字段称为“消息数据”,[它会根据您的意愿在应用程序内部显示为通知或警报对话框]

步骤 创建一个虚拟项目,然后使用firebase消息插件,在该框中,将BMW Cars作为主题,然后单击“订阅”。

现在转到您的控制台,然后发送一条消息,其中必须包含以下格式IdStatus键,因为我们正在解析ID和状态键,以便显示具有状态键的Vlaue的NextPage,但是如果您喜欢标题或正文之类的字段,则可以这样做也可以,但请务必使用颤动代码解析地图值。

//THIS IS A LITTLE BIT MODIFIED VERSION OF Example Code given in Firebase 
//Messagaing Plugin
//WHEN U PASTE THE CODE IN UR VSCODE OR ANDROID STUDIO PLEASE Format the 
//Document because it is aligned in single lines

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

void main() {
  runApp(
    new MaterialApp(
      home: new PushMessagingExample(),
      routes: <String,WidgetBuilder>{
        "/Nexpage1":(BuildContext context)=> new Nexpage1(),
        "/Nexpage2":(BuildContext context)=> new Nexpage2(),
        "/Nexpage3":(BuildContext context)=> new Nexpage3(),
        } ),);}


//INITIAL PARAMETERS
String _homeScreenText = "Waiting for token...";
bool _topicButtonsDisabled = false;
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
final TextEditingController _topicController = new TextEditingController(text: 'topic');
final Map<String, Item> _items = <String, Item>{};
Item _itemForMessage(Map<String, dynamic> message) {
  final String itemId = message['id'];
  final Item item = _items.putIfAbsent(itemId, () => new Item(itemId: itemId))..status = message['status'];
      return item;
}

//MAIN CLASS WHICH IS THE HOMEPAGE
class PushMessagingExample extends StatefulWidget {
  @override
  _PushMessagingExampleState createState() => new _PushMessagingExampleState();
}


class _PushMessagingExampleState extends State<PushMessagingExample> {
void _navigateToItemDetail(Map<String, dynamic> message) {
final String pagechooser= message['status'];
Navigator.pushNamed(context, pagechooser);
}

//CLEAR TOPIC
void _clearTopicText() {setState(() {_topicController.text = "";_topicButtonsDisabled = true;});}

//DIALOGUE
void _showItemDialog(Map<String, dynamic> message) {showDialog<bool>(context: context,builder: (_) => _buildDialog(context, _itemForMessage(message)),).then((bool shouldNavigate) {if (shouldNavigate == true) {_navigateToItemDetail(message);}});}

//WIDGET WHICH IS GOING TO BE CALLED IN THE ABOVE DIALOGUE
Widget _buildDialog(BuildContext context, Item item) {return new AlertDialog(content: new Text("Item ${item.itemId} has been updated"),actions: <Widget>[new FlatButton(child: const Text('CLOSE'),onPressed: () {Navigator.pop(context, false);},),new FlatButton(child: const Text('SHOW'),onPressed: () {Navigator.pop(context, true);},),]);}


@override
void initState() {
super.initState();
_firebaseMessaging.configure(
onLaunch: (Map<String, dynamic> message) async { _navigateToItemDetail(message);},
onResume: (Map<String, dynamic> message) async { _navigateToItemDetail(message);},
onMessage: (Map<String, dynamic> message) async {_showItemDialog(message);},);

//GETTING TOKEN FOR TESTING MANUALY
_firebaseMessaging.getToken().then((String token) {assert(token != null);setState(() {_homeScreenText = "Push Messaging token: $token";});print(_homeScreenText);});}



  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(  title: const Text('Push Messaging Demo'),),
        body: new Material(
          child: new Column(
            children: <Widget>[
              new Center(
                child: new Text(_homeScreenText),
              ),
              new Row(children: <Widget>[
                new Expanded(
                  child: new TextField(
                      controller: _topicController,
                      onChanged: (String v) {
                        setState(() {
                          _topicButtonsDisabled = v.isEmpty;
                        });
                      }),
                ),
                new FlatButton(
                  child: const Text("subscribe"),
                  onPressed: _topicButtonsDisabled
                      ? null
                      : () {
                          _firebaseMessaging
                              .subscribeToTopic(_topicController.text);
                          _clearTopicText();
                        },
                ),
new FlatButton(child: const Text("unsubscribe"),
onPressed: _topicButtonsDisabled? null: () { _firebaseMessaging.unsubscribeFromTopic(_topicController.text);
 _clearTopicText();},),

])],),));}}




//THREE DUMMY CLASSES FOR TESTING PURPOSE 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//PAGE1
class Nexpage1 extends StatefulWidget {  @override  _Nexpage1State createState() => _Nexpage1State();}
class _Nexpage1State extends State<Nexpage1> { @override Widget build(BuildContext context) { return Scaffold(body: new Center(child: new Text(" Page1"),));}}

//PAGE2
class Nexpage2 extends StatefulWidget {  @override  _Nexpage2State createState() => _Nexpage2State();}
class _Nexpage2State extends State<Nexpage2> {  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(child: new Text("2pending"),)      );  }}

//PAGE3
class Nexpage3 extends StatefulWidget {  @override  _Nexpage3State createState() => _Nexpage3State();}
class _Nexpage3State extends State<Nexpage3> {  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(child: new Text("3connected"),)      );  }}


//THIS IS THE CLASS WHICH IS USED TO PARSE THE INFORMATION
class Item {
  Item({this.itemId});
  final String itemId;
  StreamController<Item> _controller = new StreamController<Item>.broadcast();
  Stream<Item> get onChanged => _controller.stream;
  String _status;
  String get status => _status;
  set status(String value) {
    _status = value;
    _controller.add(this);
}

  static final Map<String, Route<Null>> routes = <String, Route<Null>>{};
  Route<Null> get route {
    final String routeName = '/detail/$itemId';
    return routes.putIfAbsent(
      routeName,
      () => new MaterialPageRoute<Null>(
            settings: new RouteSettings(name: routeName),
            builder: (BuildContext context) => new Nexpage3(),
          ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Ras*_*bal 6

如果您的应用程序终止,您需要使用getInitialMessage函数

 RemoteMessage terminatedMessage =
        await FirebaseMessaging.instance.getInitialMessage();

 if (terminatedMessage != null) {
     // this is a function I created to route to a page
    _processPushNotification(message: terminatedMessage);
 }
Run Code Online (Sandbox Code Playgroud)

处理推送通知函数

void _processPushNotification({@required RemoteMessage message}) async {
    print("message data ${message.data}");

    print("Processing Future after 46 seconds data ${message.data}");
    Future.delayed(Duration(
      seconds: 4,
    )).then((value) async { }); 
}
Run Code Online (Sandbox Code Playgroud)

  • 不,我想知道您如何路由到新页面。你从哪里得到上下文?或者你在哪里调用这个函数? (2认同)