FCM 到多个设备(令牌)列表

Posted

技术标签:

【中文标题】FCM 到多个设备(令牌)列表【英文标题】:FCM to multiple devices (tokens) list 【发布时间】:2020-05-01 03:51:40 【问题描述】:

我的目标是使用颤振将 FCM 消息发送到文档映射中包含的多个令牌。当前代码使用“全部发送”功能,并按预期发送给所有人。我希望插入一个widget.document.[token] 或类似的引用将只发送到文档/列表中包含的所有项目。 Firebase uses sendAll to send to specific devices so I was hoping this would work.

使用文档(令牌)引用不会返回错误,但也不会返回任何消息

使用仅包含令牌的快照会返回一个错误,仅 可以传递静态项,还有一些语法问题

使用 api/http 返回错误 posturl 返回 null

除了尝试上述方法之外,我还研究了其他人尝试过的方法。

functions/index.ts, this is designed for mulitple tokens for single user

push list programmatically yielded some syntax errors, and post url null

以下是我的一些错误:

尝试调用:

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] 未处理的异常:NoSuchMethodError:方法 '[]' 在 null 上被调用。

尝试调用:post("https://fcm.googleapis.com/fcm/send", body: "\"token\":null,

这是我的数据库结构的图片:

最后,这是我的代码:

import 'package:chat/screens2/alert_widget.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:geo_firestore/geo_firestore.dart';
import 'package:geolocator/geolocator.dart';
import 'package:chat/api/messaging.dart';
import 'package:chat/models/messages.dart';
import 'package:flutter/widgets.dart';


class SendAlert extends StatefulWidget 
  static const String id = 'send_alert';
  final Message message;

  final url;
  final body;
  final title;
  final image;
  final content;

  SendAlert(
    Key key,
    this.title,
    this.url,
    this.body,
    this.message,
    this.image,
    this.content,
    String alertIdd,
  ) : super(key: key);

  get documents => null;

  SendAlertState createState() => SendAlertState();


Firestore firestore = Firestore.instance;
GeoFirestore geoFirestore = GeoFirestore(firestore.collection('users'));

class SendAlertState extends State<SendAlert> 
  FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
  TextEditingController roomnameController = TextEditingController();
  final TextEditingController titleController = TextEditingController();
  final TextEditingController bodyController = TextEditingController();
  final List<Message> messages = [];


  TextEditingController citystateController = TextEditingController();

  final db = Firestore.instance;
  get title => null;
  get body => null;
  get uid => null;
  get alertidd => null;
  var currentLocation;
  var clients = [];

  List<Map<String, dynamic>> _documents;

  void onBbackPressed(BuildContext context) => Navigator.pop(context);


  @override
  void initState() 
    super.initState();

        populateClientu();

    Geolocator().getCurrentPosition().then((currloc) 
      setState(() 
        currentLocation = currloc;
      );
    );

    _firebaseMessaging.onTokenRefresh.listen(sendTokenToServer);
    _firebaseMessaging.getToken();

    _firebaseMessaging.subscribeToTopic('all');

    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async 
        print("onMessage: $message");
        final notification = message['notification'];
        setState(() 
          messages.add(Message(
              title: notification['title'], body: notification['body']));
        );

      ,
      onLaunch: (Map<String, dynamic> message) async 
        print("onLaunch: $message");

        final notification = message['data'];
        setState(() 
          messages.add(Message(
            title: '$notification['title']',
            body: '$notification['body']',
          ));
        );

      ,
      onResume: (Map<String, dynamic> message) async 
        print("onResume: $message");
      ,
    );
    _firebaseMessaging.requestNotificationPermissions(
        const iosNotificationSettings(sound: true, badge: true, alert: true));
  







  populateClientu() async 
    Position position = await Geolocator()
        .getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
    var queryLocation = GeoPoint(position.latitude, position.longitude);

    List<DocumentSnapshot> snapshots =
        await geoFirestore.getAtLocation(queryLocation, 10.0);

    final documents = snapshots.map((doc) 
      return doc.data;
    ).toList();

    setState(() 
      _documents = documents;
    );

  




  @override
  Widget build(BuildContext context) 
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Color.fromARGB(255, 4, 22, 36),
          title: Text('SEND MSG'),
          leading: IconButton(
            onPressed: () => this.onBbackPressed(context),
            icon: Icon(Icons.arrow_back),
          ),
        ),
        backgroundColor: Color.fromARGB(255, 4, 22, 36),
        body: 

              Container(
                  width: 250,
                  height: 35,
                  margin: EdgeInsets.only(top: 4),
                  child: Opacity(
                      opacity: 0.8,              
              child: FlatButton(

                  color: Color.fromARGB(51, 255, 255, 255),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.all(Radius.circular(10)),
                    side: BorderSide(
                      width: 0.75,
                      color: Color.fromARGB(255, 255, 255, 255),
                      style: BorderStyle.solid,
                    ),
                  ),
                  textColor: Color.fromARGB(255, 255, 255, 255),
                  padding: EdgeInsets.all(0),
                  child: Text(
                    "SEND ALERT",
                    style: TextStyle(
                      fontSize: 12,
                      letterSpacing: 2,
                      fontFamily: "Roboto",
                      fontWeight: FontWeight.w500,
                    ),
                    textAlign: TextAlign.left,
                  ),
                  onPressed: () async 



  //                const querySnapshot = await db     <--- I suspect the document map has extra unused data.  I thought maybe FCM will only accept and array of tokens, this did not work either.
    //              .collection('users')
      //            .document()
        //          .collection('token')
          //        .get();




                    sendNotification();

                    Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: (context) => AlertWidget()));
                  )))
                                );
  

  void sendTokenToServer(String fcmToken) 
    print('Token: $fcmToken');
  

  Future sendNotification() async 
  //Future sendNotification(documents(token)) async    <--- I tried to pass widget.document[token]
    final response = await Messaging.sendToAll(
      title: titleController.text,
      body: bodyController.text,
    );

    if (response.statusCode != 200) 
      Scaffold.of(context).showSnackBar(SnackBar(
        content:
            Text('[$response.statusCode] Error message: $response.body'),
      ));
    
  

当然,提前感谢大家的宝贵时间。

【问题讨论】:

不太清楚您要解决的问题是什么。您是否尝试向部分用户发送推送通知?您是否希望 Flutter 应用程序发送这些推送通知(不推荐,因为一些安全原因很容易导致您的整个数据库受到损害)?如果是这样,您需要从用户列表中获取令牌列表,然后将其发布到 FCM API。那么你在哪一部分遇到了困难? i gps 并生成用户的文档地图。该文档来自图片中的数据库。所以我使用geoHash到gps,在这种情况下我会得到2个用户。我只想向这两个用户发送 gps。在更大的范围内可能有 100 个用户,但只有 8 个在 gps 过滤范围内,所以我只想发送给这 8 个用户/令牌。你提到了从颤振发送的安全问题,这就是我试图做的,类似于发送所有。我不知道如何使用api发送我目前拥有的文档图。我确实尝试了该 api,但没有成功。 您可以将令牌存储在firestore中并使用firebase功能。检查这个skcript.com/svr/… 我正在尝试firebase函数方法。 @vic 嗨,你能找到答案吗?我正在尝试做类似的事情。创建文档后,我需要按位置过滤用户并向这些用户发送通知。谢谢! 【参考方案1】:
    您的 firestore 实例返回用户文档列表。您需要遍历文档以仅提取文档中的标记并将它们放入字符串列表中,然后将其传递给 FCM。

如果您比较您的屏幕截图和应该返回文档的代码。他们不对齐。你可以改变它。

//                const querySnapshot = await db     <--- I suspect the document map has extra unused data.  I thought maybe FCM will only accept and array of tokens, this did not work either.
    //              .collection('users')
      //            .document()
        //          .collection('token')
          //        .get();

  //                const querySnapshot = await db     <--- I suspect the document map has extra unused data.  I thought maybe FCM will only accept and array of tokens, this did not work either.
    //              .collection('users')
      //            .document("DOCUMENT ID")
          //        .get();

上面会得到一个文件。然后,您可以映射文档并获取单个令牌,您可以将其传递到消息组件的令牌字段中。

    您还没有将令牌传递给实际的异步函数 Messaging.sendToAll

    最终响应 = 等待 Messaging.sendToAll( 标题:titleController.text, 正文:bodyController.text, );

上面应该在正文中包含一个标记字符串,如下所示。

final response = await Messaging.sendToAll(
      title: titleController.text,
      body: bodyController.text,
      token:fcmTokenReturnedFromFirestore
    );

【讨论】:

这是否允许从具有多个令牌的文档发送多个令牌,还是我必须多次发送?

以上是关于FCM 到多个设备(令牌)列表的主要内容,如果未能解决你的问题,请参考以下文章

如何识别和删除服务器上过期的 FCM 令牌?

fcm 注册令牌自动存储到 firebase 数据库中

推送通知的 Amazon SNS android 设备令牌限制

Flutter FCM iOS 问题 - 在检索 FCM 令牌之前未设置 APNS 设备令牌

FCM 令牌是特定于应用程序还是设备?

注册令牌不是有效的 FCM 注册令牌