簡體   English   中英

如何在點擊 flutter 的推送通知時打開特定屏幕

[英]how to open particular screen on clicking on push notification for flutter

我試圖在點擊推送通知時打開特定屏幕,我的有效負載如下所示:

 var payload = {
        notification: {
            title: notificationTitle,
            body: notificationMessage,
            click_action:"/screena",sound:"default",
        }
    };

我收到通知,但我無法在 flutter 中捕獲通知單擊事件如何捕獲它。 我正在使用下面鏈接的 firebase 消息 package:

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);
 });
 }

在這里,當我的應用程序位於前台時,我可以收到@xqwzts 在消息中所說的消息,但我的問題是如何從系統托盤中引發的推送通知中捕獲點擊事件並導航到所需的屏幕。

這里有幾件事:

1- click_action必須設置為"FLUTTER_NOTIFICATION_CLICK"

2- click_action必須在有效載荷的data部分中設置

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>"
}'

這應該允許您在 flutter 應用程序的onMessage處理程序中接收消息。

從那里你可以調用Navigator.of(context).pushNamed(message['screen'])

如果你沒有一個BuildContext在這一點上,你可以注冊一個GlobalKey作為navigatorKey你的財產MaterialApp ,並用它來訪問你的Navigator全球范圍內,通過GlobalKey.currentState

由於@xqwzts 方法適用於在 App 上接收消息是打開狀態,

以下示例將導航到特定頁面,

[代碼僅取自 FIREBASE 消息插件示例代碼,它導航到指定頁面,其中包含我們通過 FIREBASE 控制台發送的數據]

//eg: if you give /Nexpage3  in the status field then it will navigate to Nextpage3 of your App

在此處輸入圖片說明

了解 2 件事,FCM 通知有 2 部分

Firebase 雲消息傳遞頁面中的第一個消息標題部分稱為通知數據[當應用程序最小化或關閉時,它將顯示為通知]

網頁底部的第二個消息標題部分稱為消息數據,[它將在應用程序內部顯示為通知或警報對話框,這取決於您的意願]

步驟創建一個虛擬項目,然后使用 firebase 消息插件,並在該框中將 BMW Cars 設為 atopic 並單擊訂閱

現在轉到您的控制台,然后使用以下格式發送一條消息,它必須包含IdStatus鍵,因為我們正在解析 Id 和狀態鍵以顯示具有狀態鍵值的 NextPage 但如果您更喜歡標題或正文等字段您也可以這樣做,但請確保在您的顫振代碼中解析地圖值。

//THIS IS A LITTLE BIT MODIFIED VERSION OF Example Code given in Firebase 
//Messaging Plugin
//WHEN U PASTE THE CODE IN UR VS CODE 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(),
          ),
    );
  }
}

要提供有效載荷,請使用:

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>();
}

轉到您的 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(),
      ),
    );
  }
}

當您按下應用欄中的通知時,它會調用 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"],
                      )));
        });
      },
    );
  }

第 1 步:click_action通知中傳遞一個鍵值對作為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(),
      ),
    );
  }
}
  • 現在,從onResumeonLuanch方法使用以下代碼行導航到您的屏幕:
 navigatorKey.currentState.push(
    MaterialPageRoute(builder: (_) => Dashboard())
 );

對於任何想要在 Null Safety(適用於 iOS 和 Android)之前遷移到最新版本的 Firebase Messaging 的人,以下是步驟

pubspec.yaml

firebase_core: ^0.7.0
firebase_messaging: ^8.0.0-dev.15

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: ...

   );
  }

}

主屏幕.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,
                            )));
        });
      }
    }

注意到 iOS 通知會出現在頂部(抬頭顯示)和onMessage (當應用在前台時)和onBackgroundMessage (當應用在后台或終止時)的觸發方法。

對於 android 通知將出現在頂部的托盤和onBackgroundMessage觸發方法(當應用程序在后台或終止時)僅。 您必須使用第三方解決方案,如flutter_local_notificationsoverlay_supportonMessage期間顯示通知(前台應用程序)。

對於 iOS,當點擊通知時(當應用在后台、終止或前台時),將觸發onMessageOpenedApp方法。 對於 Android,此方案僅在應用程序在后台運行或終止時才有效(如果啟用了抬頭通知顯示/橫幅 - 您需要為 android 創建 channel_id)

您不再需要在數據負載上發送click_action: FLUTTER_CLICK_ACTION以便在通知上有可點擊的事件。 FirebaseMessaging 將為您處理

 FirebaseMessaging.instance.getInitialMessage().then((message) {
  RemoteNotification notification = message.notification;
  AndroidNotification android = message.notification?.android;
  if (notification != null && android != null) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => NotificationScreen(
          name: message.data['name'],
          place: message.data['place'],
          address: message.data['address'],
        ),
      ),
    );
  }
});// handles notification clicks while the app is in the terminated state

最初,@xqwzts 回答對我不起作用。 經過大量研究,我發現我們需要初始化configure方法的延遲很小。 我附上了下面的代碼。 它會對其他人有幫助。

void initState() {
        // TODO: implement initState
        super.initState();
        _firebaseMsgListener();
      }

      void _firebaseMsgListener() {
        // if (Platform.isIOS) iOS_Permission();

        _firebaseMessaging.getToken().then((token) {
          Fimber.d("=====> token : $token");
          UpdateTokenRequest request = UpdateTokenRequest();
          request.token = token;
          homeBloc.updateFCMToken(request);
        });

        Future.delayed(Duration(seconds: 1), () {
          _firebaseMessaging.configure(
            onBackgroundMessage: myBackgroundMessageHandler,
            onMessage: (Map<String, dynamic> message) async {
              Fimber.d("=====>on message $message");
              Fluttertoast.showToast(msg: "onMessage $message");
            },
            onResume: (Map<String, dynamic> message) async {
              Fimber.d("=====>onResume $message");
              Fluttertoast.showToast(msg: "onResume $message");
            },
            onLaunch: (Map<String, dynamic> message) async {
              Fimber.d("=====>onLaunch $message");
              Fluttertoast.showToast(msg: "onLaunch $message");
            },
          );
        });
      }

      Future<dynamic> myBackgroundMessageHandler(
          Map<String, dynamic> message) async {
        print("_backgroundMessageHandler");
        if (message.containsKey('data')) {
          // Handle data message
          final dynamic data = message['data'];
          print("_backgroundMessageHandler data: ${data}");
        }

        if (message.containsKey('notification')) {
          // Handle notification message
          final dynamic notification = message['notification'];
          print("_backgroundMessageHandler notification: ${notification}");
          Fimber.d("=====>myBackgroundMessageHandler $message");
        }
        return Future<void>.value();
      }

AndroidManifest.xml

這對我有用。 您需要在 AndroidManifest.xml 文件中添加此代碼。 您可以將任何內容作為“click_action”,但它需要與清單文件中的相同。 不確定iOS。

我來不及回答這個問題,但最后,我已經用一些 android 原生代碼和顫振代碼實現了這一點。 所以,讓我們從頭開始一步一步

1.) 轉到您的 build.gradle 文件(路徑android>app>buid.gradle )。 添加 firebase 消息傳遞依賴項,然后從 android studio 同步文件

implementation 'com.google.firebase:firebase-messaging'

2.) 在 MainActivity 文件路徑 (PATH android>app>src>main>kotlin>com>yourpackage>MyApplication.java ) 中創建一個新文件名 MyApplication.java

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;

import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;
import io.flutter.plugins.pathprovider.PathProviderPlugin;

public class MyApplication extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback {

    @Override
    public void onCreate() {
        super.onCreate();
        this.createChannel();
        FlutterFirebaseMessagingService.setPluginRegistrant(this);
    }

    @Override
    public void registerWith(PluginRegistry registry) {
        io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
        PathProviderPlugin.registerWith(registry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
    }

    private void createChannel(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String name = getString(R.string.default_notification_channel_id);
            NotificationChannel channel = new NotificationChannel(name, "default", NotificationManager.IMPORTANCE_HIGH);
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.createNotificationChannel(channel);
        }
    }
}

3.) 轉到您的 android 應用清單文件 (PATH android>app>src>main>AndroidManifest.xml ) 並添加將<application android:name with ".MyApplication"替換為<application android:name with ".MyApplication"標簽

<application
        android:name=".MyApplication" //replace your name with .MyApplication
        android:label="helpwise"
        android:icon="@mipmap/ic_launcher">

4.) 現在您需要在flutter 項目中添加firebase 消息傳遞依賴項。 所以添加在 pubspec.yaml

firebase_messaging: ^6.0.9

5.) 在 main.dart 文件中添加 firebase 代碼

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
  if (message.containsKey('data')) {
    final dynamic data = message['data'];
    print('Notification data is ');
    print(message['data']);
  }

  if (message.containsKey('notification')) {
    // Handle notification message
    final dynamic notification = message['notification'];
  }
}

class SelectMailbox extends StatefulWidget {
  static const routeName = '/mailbox-screen';

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

class _SelectMailboxState extends State<SelectMailbox> {
  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();

  @override
  void initState() {
    
    _firebaseMessaging.getToken().then((token) async{
      SharedPreferences preferences = await SharedPreferences.getInstance();
      final String userData = preferences.get('userData');
      final String authToken=jsonDecode(userData)['token'];
      print("Device token is $token"); //I will use this token to send notif.
      await http.post("https://your_domain/mobile/save-token",
          headers: {"Content-Type": "application/json"},
          body: jsonEncode({"device_token": token,"type":"android","token":authToken}));
    });

    _firebaseMessaging.configure(
        onMessage: (Map<String, dynamic> message) async {
         // 
        }, onBackgroundMessage: Platform.isAndroid?myBackgroundMessageHandler:null,
        onResume: (Map<String, dynamic> message) async {
           print("onBackground Message $message"); 
          _selectIdsNotification(message['data']['thread_id'],message['data']['mailbox_id'],14,message['data']['mailboxType'],"All",context);
        }, onLaunch: (Map<String, dynamic> message) async {
      print("onLaunch Message $message");
      _selectIdsNotification(message['data']['thread_id'],message['data']['mailbox_id'],14,message['data']['mailboxType'],"All",context);
    });

    super.initState();
  }

  _selectIdsNotification(threadID,mailboxId,subBox,mailboxType,mailboxTab,myContext) async {
    // YOU CAN ADD THE LOGIC OF DIFFERENT PAGE ROUTE ACCORDING TO DATA PASS FROM NOTIFICATION in my case i could use the mailboxType
    Navigator.push(
      myContext,
      MaterialPageRoute(
        builder: (context) => ThreadDetail(threadID, mailboxType,notificationMailboxId: mailboxId),
      ),
    );
  }

6.) 再次轉到您的 AndoridManifest.file 並在活動關閉標記之后的活動標記和元數據標記代碼中添加意圖過濾器代碼

    <application
            android:name=".MyApplication"
            android:label="helpwise"
            android:icon="@mipmap/ic_launcher">
            <activity
                android:name=".MainActivity"
                android:launchMode="singleTop"
                android:theme="@style/LaunchTheme"
                android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
                android:hardwareAccelerated="true"
                android:windowSoftInputMode="adjustResize">
               
                <meta-data
                  android:name="io.flutter.embedding.android.NormalTheme"
                  android:resource="@style/NormalTheme"
                  />
                <meta-data
                  android:name="io.flutter.embedding.android.SplashScreenDrawable"
                  android:resource="@drawable/launch_background"
                  />
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
    <-- ADD THIS INTENT FILTER IN YOUR CODE -->
                <intent-filter>
                    <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
    
            </activity>
            <meta-data
                android:name="flutterEmbedding"
                android:value="2" />
 <-- ADD THIS META DATA TAG IN YOUR CODE -->
            <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id"
                       android:value="@string/default_notification_channel_id" />
        </application> 

7.) 現在轉到 android 值文件夾 PATH( android>app>src>main>res>values>strings.xml )。 如果你沒有看到strings.xml文件,那么創建一個與strings.xml文件相同路徑的文件並添加下面的代碼

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="default_notification_channel_id">default_notification_channel_id</string>
</resources>

10.) 那是它的家伙。 現在,您需要重新啟動應用程序並將 firebase 消息通知 API 調用到設備令牌。

    ar axios = require('axios');
var data = JSON.stringify(
{
  "to": "your_mobile_device_token",
  "data": {
    "mailbox_id": "11111",
    "thread_id": "1111",
    "mailboxType": "email",
    "click_action": "FLUTTER_NOTIFICATION_CLICK"
  },
  "priority": "high",
  "notification": {
    "body": "Hi, You have received new Message",
    "title": "Flutter",
    "image": "your_image_cdn_path"
  },
  "click_action": "FLUTTER_NOTIFICATION_CLICK"
});

var config = {
  method: 'post',
  url: 'https://fcm.googleapis.com/fcm/send',
  headers: { 
    'Authorization': 'key=your_firebase_server_key', 
    'Content-Type': 'application/json'
  },
  data : data
};

axios(config)
.then(function (response) {
  console.log(JSON.stringify(response.data));
})
.catch(function (error) {
  console.log(error);
});

在處理推送通知重定向代碼之前,有幾件事需要注意。

首先,在推送通知有效負載的數據塊中添加“click_action”:“FLUTTER_NOTIFICATION_CLICK”。

其次,在 firebasehelper class 中定義以下回調。

//This method will call when the app is in kill state
    FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
      if (message != null) {
        //Handle push notification redirection here
      }
    });

    //This method will call when the app is in foreground state
    FirebaseMessaging.onMessage.listen((RemoteMessage? message) async {
      if (message != null && message.data.isNotEmpty) {
        //Handle push notification redirection here
      }
    });

    //This method will call when the app is in background state
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage? message) {
      if (message != null) {
        //Handle push notification redirection here
      }
    });

Firebase幫手 class代碼

class FirebaseHelper {
  static late FirebaseApp _firebaseApp;
  static late FirebaseMessaging _firebaseMessaging;

  static final FirebaseHelper _singleton = FirebaseHelper._internal();
  static late Timer _timer;

  factory FirebaseHelper() {
    return _singleton;
  }

  FirebaseHelper._internal();

  // To Initialize Firebase
  static Future<void> init() async {
    _firebaseApp = await Firebase.initializeApp();
    await _initCloudMessaging();
  }

  static FirebaseApp getFireBaseApp() {
    return _firebaseApp;
  }

  // To Initialize Firebase FCM
  static Future<void> _initCloudMessaging() async {
    _firebaseMessaging = FirebaseMessaging.instance;
    _firebaseMessaging.setForegroundNotificationPresentationOptions(sound: true, badge: true);
    await requestNotificationPermissions();
    _setUpNotificationListener();
  }

  static Future<NotificationSettings> getNotificationSettings() async {
    return await FirebaseMessaging.instance.getNotificationSettings();
  }

  // To Request Notification Permissions (For IOS)
  static Future<NotificationSettings> requestNotificationPermissions() async {
// for permission
    return await FirebaseMessaging.instance.requestPermission(
      alert: true,
      badge: true,
      provisional: false,
      sound: true,
    );
  }

  // To Set On Notification Listener
  static void _setUpNotificationListener() {
    //This method will call when the app is in kill state
    FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
      if (message != null) {
        //Handle push notification redirection here
      }
    });

    //This method will call when the app is in foreground state
    FirebaseMessaging.onMessage.listen((RemoteMessage? message) async {
      if (message != null && message.data.isNotEmpty) {
        //Handle push notification redirection here
      }
    });

    //This method will call when the app is in background state
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage? message) {
      if (message != null) {
        //Handle push notification redirection here
      }
    });
  }

  // To Get Device Token
  static Future<String?> getDeviceToken() async {
    return await _firebaseMessaging.getToken();
  }
  
}

並且您需要在 main.dart 文件中的 runApp function 之前初始化此 class,如下所示。

 WidgetsFlutterBinding.ensureInitialized();
  FirebaseHelper.init();

從包的文檔( https://pub.dev/packages/firebase_messaging )中,它明確指出您需要在 android/app/src/main/java/app/{{appName} 中創建一個 Application.java } 地點。

在該文件 (Application.java) 中,它指定了您需要包含的確切代碼。

完成后,對於 flutter 代碼,請確保在 main.dart 文件中設置 backgroundMessageHandler 函數,因為它需要是頂級函數。

還有一點要注意,如果您的 PluginRegistry 出現錯誤,請使用以下代碼:

import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));

當您確實發送通知並且希望您的 backgroundMessage 在單擊通知時捕獲該通知以及 onResume 捕獲它時,請確保您的通知具有標題、正文、click_action,然后您的數據也應具有標題、正文、然后你想要的任何數據。

我在我自己的例子中使用了這個:

$notification = [
        'title' => 'New notification',
        'body' => "{$group->group_name} needs it's members to be updated. It starts on {$startDate->toFormattedDateString()}, however...",
        'icon' => '',
        'click_action'=> 'FLUTTER_NOTIFICATION_CLICK',
        'Test' => 'Some testing data',
    ];
    $data = [
        'title' => 'This title is in the data',
        'body' => "this body is in the data",
        'Test' => 'Some testing data',
        'click_action'=> 'FLUTTER_NOTIFICATION_CLICK'
    ];

如果您的應用程序終止,您需要使用getInitialMessage函數

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

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

檢查已安裝的 state,如果未安裝,則調用

if(!mounted) {
   setState(() {});
}

這將使小部件掛載並獲取上下文,但請確保在導航到新屏幕之前調用它以檢查上下文是否可用

使用 firebase_messaging: "^8.0.0-dev.10" FlutterFire FCM FlutterFire FCM 通知

遵循此代碼。 這要容易得多。

class Application extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Application();
}

class _Application extends State<Application> {
  @override
  void initState() async {
    super.initState();

    // Get any messages which caused the application to open from
    // a terminated state.
    RemoteMessage initialMessage =
        await FirebaseMessaging.instance.getInitialMessage();

    // If the message also contains a data property with a "type" of "chat",
    // navigate to a chat screen
    if (initialMessage?.data['type'] == 'chat') {
      Navigator.pushNamed(context, '/chat',
          arguments: ChatArguments(initialMessage));
    }

    // Also handle any interaction when the app is in the background via a
    // Stream listener
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      if (message.data['type'] == 'chat') {
        Navigator.pushNamed(context, '/chat',
          arguments: ChatArguments(message));
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text("...");
  }
}

我在點擊通知時移動到特定屏幕onResume和onLaunch。但是當我的應用程序終止時我仍然有問題,我點擊通知,我的屏幕打開,幾秒鍾后我的默認應用程序屏幕打開。祝你好了。

      onBackgroundMessage: myBackgroundMessageHandler,
  onLaunch: (Map<String, dynamic> message) async {
    await print("onLaunch: $message");
    ///_navigateToItemDetail(message);
    await _showItemDialog(message); // Diyalog Kutusu Oluştur
  },
  onResume: (Map<String, dynamic> message) async {
    await print("onResume: $message");
    ///_navigateToItemDetail(message);      
    await _showItemDialog(message); // Diyalog Kutusu Oluştur
  },

添加等待正在工作

    onLaunch: (Map<String, dynamic> message) {
      print("I am here onLaunch");
      print(message);
      Navigator.push(
       context,
       MaterialPageRoute(
        builder: (context) => ScreenA()
       )
      );
    },
    onResume: (Map<String, dynamic> message) {
      print("I am here onResume");
      print(message);
      Navigator.push(
       context,
       MaterialPageRoute(
        builder: (context) => ScreenA()
       )
      );
    },

試試這個

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM