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

6

我的目标是使用Flutter向包含在文档映射中的多个令牌发送FCM消息。当前代码使用“发送全部”功能,它会按预期发送到所有人。我希望插入一个widget.document.[token]或类似引用,只发送给包含在文档/列表中的所有项。

Firebase使用sendAll发送到特定设备,所以我希望这个方法行得通。
  • 使用document(token)引用不会返回错误,但也没有消息

  • 使用仅包含令牌的快照会返回错误,指出只能传递静态项目以及一些语法问题

  • 使用api/http会返回posturl为null的错误

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

以下是我的一些错误:

  • 尝试调用:

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

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

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

Firebase Database Structure

最后,这是我的代码:

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

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


2
不太清楚你要解决的问题是什么。你是想向用户子集发送推送通知吗?你想让Flutter应用程序发送这些推送通知(不建议这样做,因为有几个安全原因很容易导致整个数据库被攻击泄漏)吗?如果是这样,你需要从用户列表中获取标记列表,然后将其POST到FCM API中。那么你在哪个部分遇到了困难呢? - Ovidiu
我需要使用 GPS 并生成用户的文档地图。该文档来自图片中的数据库。因此,我使用 geoHash 转换为 GPS,这样我就可以得到 2 个用户。我只想发送 GPS 给这两个用户。在更大的范围内可能有 100 个用户,但只有 8 个在 GPS 过滤范围内,所以我只想发送给这 8 个用户/令牌。您提到了从 Flutter 发送时的安全问题,这正是我尝试做的事情,类似于发送全部。我不知道如何使用 API 发送我当前拥有的文档地图。我曾尝试过 API,但没有成功。 - v i c
你可以将令牌存储在Firestore中并使用Firebase函数。请查看此链接:https://www.skcript.com/svr/sending-push-notification-with-firebase-firestore-cloud-functions/ - mkf
我正在尝试使用Firebase函数方法。 - v i c
@vic 你好,你找到答案了吗?我正在尝试做类似的事情。在创建文档时,我需要按位置过滤用户并向这些用户发送通知。谢谢! - RL Shyam
1个回答

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

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

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

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

上述代码应该在正文中包含一个令牌字符串,如下所示。

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

这会允许从具有多个令牌的文档中发送多个令牌吗?还是我必须多次发送它? - v i c

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接