【问题标题】:Flutter: How can i send push notification programmatically with fcmFlutter:如何使用 fcm 以编程方式发送推送通知
【发布时间】:2019-06-20 03:52:09
【问题描述】:

我正在创建一个聊天应用程序,如果此人有新消息,我想使用 fcm 发送通知,但我不知道如何继续。我发现的所有教程都用于从 firebase 发送消息。但是我想在有新消息给这个人时自动发送它

【问题讨论】:

标签: android flutter google-cloud-firestore


【解决方案1】:

我将在这里列出一些我参与的相关问题并提供答案。我想您会发现很多有关在聊天应用中使用 Firebase 云消息传递 (FCM) 的相关信息。

  1. Is FCM the only way to build a chat app ?
  2. Suggested approach to use FCM in a chat app
  3. Is using topics a better solution then using the fcmToken in a chat app?
  4. Problems with FCM onMessage while app is in background
  5. Problem: after logoff, user continues to receive notifications

祝你好运!

【讨论】:

    【解决方案2】:

    如果您使用 firebase,可能的解决方法应该是这样的:

    您需要为特定用户存储每个 Firebase FCM 令牌(此处需要考虑到用户可以从多个设备同时登录其帐户),以便您可以存储 userId em> 和他的 deviceUniqueId on flutter 你可以从 device_info https://pub.dev/packages/device_info 获得它:

      String identifier;
      final DeviceInfoPlugin deviceInfoPlugin = new DeviceInfoPlugin();
      try {
        if (Platform.isAndroid) {
          var build = await deviceInfoPlugin.androidInfo;
          identifier = build.id.toString();
        } else if (Platform.isIOS) {
          var data = await deviceInfoPlugin.iosInfo;
          identifier = data.identifierForVendor;//UUID for iOS
        }
      } on PlatformException {
        print('Failed to get platform version');
      }
    

    然后要获取 Firebase 为每个设备提供的唯一 CFM 令牌,您可以使用 Firebase firebase_messaging 插件 (https://pub.dev/packages/firebase_messaging) getToken() 获取它并将令牌插入到 Firestore(或您要存储它的其他数据库)

      FirebaseMessaging firebaseMessaging = new FirebaseMessaging();
    
      firebaseMessaging.requestNotificationPermissions(
          const IosNotificationSettings(sound: true, badge: true, alert: true));
      firebaseMessaging.onIosSettingsRegistered
          .listen((IosNotificationSettings settings) {
        print("Settings registered: $settings");
      });
    
      firebaseMessaging.getToken().then((token){
    
        print('--- Firebase toke here ---');
        Firestore.instance.collection(constant.userID).document(identifier).setData({ 'token': token});
        print(token);
    
      });
    

    之后,您可以为一个用户插入一个或多个连接到多个设备的 FCM 令牌。 1 个用户……n 个设备,1 个设备……1 个唯一令牌,用于从 Firebase 获取推送通知。

    当有新消息发送给该人时自动发送:现在您需要调用 Firestore API(确实非常快,但需要注意您在此处使用的计划限制) 或另一个 API 调用(如果您将令牌存储到另一个数据库),以便为每个用户获取令牌/令牌并发送推送通知。

    要从 Flutter 发送推送通知,您可以使用 Future 异步函数。 Ps:我在这里传递一个 List 作为参数,以便使用 "registration_ids" 而不是 "to" 并将推送通知发送到多个令牌,如果用户已经在多个设备上登录。

    Future<bool> callOnFcmApiSendPushNotifications(List <String> userToken) async {
    
      final postUrl = 'https://fcm.googleapis.com/fcm/send';
      final data = {
        "registration_ids" : userToken,
        "collapse_key" : "type_a",
        "notification" : {
          "title": 'NewTextTitle',
          "body" : 'NewTextBody',
        }
      };
    
      final headers = {
        'content-type': 'application/json',
        'Authorization': constant.firebaseTokenAPIFCM // 'key=YOUR_SERVER_KEY'
      };
    
      final response = await http.post(postUrl,
          body: json.encode(data),
          encoding: Encoding.getByName('utf-8'),
          headers: headers);
    
      if (response.statusCode == 200) {
        // on success do sth
        print('test ok push CFM');
        return true;
      } else {
        print(' CFM error');
        // on failure do sth
        return false;
      }
    }
    

    您还可以检查邮递员的邮递电话以进行一些测试。 POST 请求 在标题上添加:

    1. key 授权 value key=AAAAO........ // 项目概览 -> 云消息传递 -> 服务器密钥
    2. key Content-Type with value application/json

    并在正文上添加

    {
     "registration_ids" :[ "userUniqueToken1", "userUniqueToken2",... ],
     "collapse_key" : "type_a",
     "notification" : {
         "body" : "Test post",
         "title": "Push notifications E"
     }
    }
    

    "registration_ids" 将其发送到多个令牌(同一用户同时登录多个设备) “to” 以便将其发送到单个令牌(每个用户一个设备/或始终更新与他的设备连接的用户令牌并具有 1 个令牌...1 个用户)

    我正在对响应进行编辑,以便在受信任的环境或服务器上添加FCM 服务器密钥非常重要!

    【讨论】:

    • firebaser here 在客户端代码中使用"key=your_server_key" 是一个严重的安全风险,因为它允许恶意用户向您的用户发送他们想要的任何消息。这是一种不好的做法,不应在生产级应用程序中使用。如需更好的方法,请参阅fireship.io/lessons/flutter-push-notifications-fcm-guide 和我的答案:stackoverflow.com/a/57842615
    • 是的,这个也是为了开发目的,就像上面的邮递员示例一样,我正在编辑答案,因为我也在服务器上设置了服务器密钥,当调用 API 时!跨度>
    • 我们如何做到这一点'这里需要考虑到用户可以从多个设备同时登录他的帐户'
    • 当您可以保存需要使用的元数据但确实可以使用另一个数据库时,我以使用 Cloud Firestore 为例。您可以在 Collection 上保存用户 ID,在每个 Document 上使用 device_info pub.dev/packages/device_info 在 Flutter 上的每个设备中唯一的设备 ID,您可以非常简单地获得该信息在 Collection 上,您可以存储 Firebase 令牌,以便在该设备上发送推送通知。这样,1 次使用可以有多个设备,每个设备将有一个令牌。抱歉迟到了
    • 我的 firebase 项目中目前有 2 个 android 应用,如何指定通知与哪个应用相关?
    【解决方案3】:
        //Notification Sending Side Using Dio flutter Library to make http post request
    
            static Future<void> sendNotification(receiver,msg)async{
    
            var token = await getToken(receiver);
            print('token : $token');
    
            final data = {
              "notification": {"body": "Accept Ride Request", "title": "This is Ride Request"},
              "priority": "high",
              "data": {
                "click_action": "FLUTTER_NOTIFICATION_CLICK",
                "id": "1",
                "status": "done"
              },
              "to": "$token"
            };
    
            final headers = {
              'content-type': 'application/json',
              'Authorization': 'key=AAAAY2mZqb4:APA91bH38d3b4mgc4YpVJ0eBgDez1jxEzCNTq1Re6sJQNZ2OJvyvlZJYx7ZASIrAj1DnSfVJL-29qsyPX6u8MyakmzlY-MRZeXOodkIdYoWgwvPVhNhJmfrTC6ZC2wG7lcmgXElA6E09'
            };
    
    
            BaseOptions options = new BaseOptions(
              connectTimeout: 5000,
              receiveTimeout: 3000,
              headers: headers,
            );
    
    
            try {
              final response = await Dio(options).post(postUrl,
                  data: data);
    
              if (response.statusCode == 200) {
                Fluttertoast.showToast(msg: 'Request Sent To Driver');
              } else {
                print('notification sending failed');
                // on failure do sth
              }
            }
            catch(e){
              print('exception $e');
            }
    
    
    
    
          }
    
          static Future<String> getToken(userId)async{
    
            final Firestore _db = Firestore.instance;
    
            var token;
            await _db.collection('users')
                .document(userId)
                .collection('tokens').getDocuments().then((snapshot){
                  snapshot.documents.forEach((doc){
                    token = doc.documentID;
                  });
            });
    
            return token;
    
    
          }
    
    
        //Now Receiving End 
    
            class _LoginPageState extends State<LoginPage>
            with SingleTickerProviderStateMixin {
    
          final Firestore _db = Firestore.instance;
          final FirebaseMessaging _fcm = FirebaseMessaging();
    
          StreamSubscription iosSubscription;
    
    
    
        //this code will go inside intiState function 
    
        if (Platform.isIOS) {
              iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
                // save the token  OR subscribe to a topic here
              });
    
              _fcm.requestNotificationPermissions(IosNotificationSettings());
            }
            _fcm.configure(
              onMessage: (Map<String, dynamic> message) async {
                print("onMessage: $message");
                showDialog(
                  context: context,
                  builder: (context) => AlertDialog(
                    content: ListTile(
                      title: Text(message['notification']['title']),
                      subtitle: Text(message['notification']['body']),
                    ),
                    actions: <Widget>[
                      FlatButton(
                        child: Text('Ok'),
                        onPressed: () => Navigator.of(context).pop(),
                      ),
                    ],
                  ),
                );
              },
              onLaunch: (Map<String, dynamic> message) async {
                print("onLaunch: $message");
                // TODO optional
              },
              onResume: (Map<String, dynamic> message) async {
                print("onResume: $message");
                // TODO optional
              },
            );
    
    //saving token while signing in or signing up
     _saveDeviceToken(uid) async {
        // FirebaseUser user = await _auth.currentUser();
    
        // Get the token for this device
        String fcmToken = await _fcm.getToken();
    
        // Save it to Firestore
        if (fcmToken != null) {
          var tokens = _db
              .collection('users')
              .document(uid)
              .collection('tokens')
              .document(fcmToken);
    
          await tokens.setData({
            'token': fcmToken,
            'createdAt': FieldValue.serverTimestamp(), // optional
            'platform': Platform.operatingSystem // optional
          });
        }
      }
    

    【讨论】:

    • 你的答案背景我理解,但代码不清楚。您使用了哪些 Firebase 组件。如果您可以在此页面中添加包含的文件并且还想知道您为什么使用 DIO 插件,那就更好了。
    猜你喜欢
    • 2017-01-12
    • 1970-01-01
    • 1970-01-01
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    • 1970-01-01
    相关资源
    最近更新 更多