在Flutter中向特定用户发送Firebase通知

9

当一个用户按下按钮时,我如何向另一个用户发送通知?能否有人展示一段代码片段?请注意,之前已经有人问过这个问题,但因为有“多个答案”,所以被关闭了。提供的相似链接没有解释在 Flutter 中如何发送通知。

2个回答

18

我已经找到了如何使用应用内功能向另一个设备发送通知的方法。

首先,您需要导入必要的包:

firebase_messaging
flutter_local_notifications

注意:您还将使用http

还要注意:要向另一个设备发送通知,您必须知道该设备的设备令牌。我建议获取令牌并将其保存在Firestore或实时数据库中。以下是获取设备令牌的代码。

String? mtoken = " ";

void getToken() async {
    await FirebaseMessaging.instance.getToken().then((token) {
      setState(() {
        mtoken = token;
      });
    });
  }

令牌将保存在mtoken中,您现在可以将其用作接下来步骤的令牌。

发送通知的代码

在Firebase项目中启用Firebase Cloud Functions,这必须使用Firbebase Blaze计划。

在我的functions文件夹中,我在index.js中添加了此代码

 /* eslint-disable */
const functions = require("firebase-functions");
const admin = require("firebase-admin");

admin.initializeApp();

exports.sendNotification = functions.https.onCall(async (data, context) => {
  await admin.messaging().sendMulticast({
  tokens: data.tokens,
  notification: {
    title: data.title,
    body: data.body,
    imageUrl: data.imageUrl,
  },
});


确保部署您的 Firebase 云函数。如果您看到这个,那么您就知道它是否起作用了。

enter image description here

您可以使用以下代码在Flutter应用程序中调用此函数

Future<void> sendNotification(
  tokens,
  String title,
  String body,
  String imageUrl,
) async {
  FirebaseFunctions functions =
      FirebaseFunctions.instanceFor(region: 'us-central1');

  try {
    final HttpsCallable callable = functions.httpsCallable('sendNotification');
    final response = await callable.call({
      'tokens': tokens,
      'title': title,
      'body': body,
      'imageUrl': imageUrl,
    });

    print('Message sent: ${response.data}');
  } catch (e) {
    print('Error sending message: $e');
  }
}

接收通知的代码

下一步是请求发送推送通知到您的应用程序的权限。

  void requestPermission() async {
    FirebaseMessaging messaging = FirebaseMessaging.instance;

    NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: false,
      provisional: false,
      sound: true,
    );

    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      print('User granted permission');
    } else if (settings.authorizationStatus ==
        AuthorizationStatus.provisional) {
      print('User granted provisional permission');
    } else {
      print('User declined or has not accepted permission');
    }
  }

如果您在控制台中收到“用户已拒绝或未接受权限”的消息,请尝试退出应用程序,找到主屏幕上的图标,按住应用程序图标,点击“应用信息”,点击“通知”并打开“所有[应用名称]通知”。

您还需要两个函数来加载Firebase Cloud Messaging通知以及一个监听通知的函数。

加载Firebase Cloud Messaging通知的代码:

 void loadFCM() async {
    if (!kIsWeb) {
      channel = const AndroidNotificationChannel(
        'high_importance_channel', // id
        'High Importance Notifications', // title
        importance: Importance.high,
        enableVibration: true,
      );

      flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

      /// Create an Android Notification Channel.
      ///
      /// We use this channel in the `AndroidManifest.xml` file to override the
      /// default FCM channel to enable heads up notifications.
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>()
          ?.createNotificationChannel(channel);

      /// Update the iOS foreground notification presentation options to allow
      /// heads up notifications.
      await FirebaseMessaging.instance
          .setForegroundNotificationPresentationOptions(
        alert: true,
        badge: true,
        sound: true,
      );
    }
  } 

同时,这个函数用来监听 Firebase Cloud Messaging 通知。

void listenFCM() async {
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      RemoteNotification? notification = message.notification;
      AndroidNotification? android = message.notification?.android;
      if (notification != null && android != null && !kIsWeb) {
        flutterLocalNotificationsPlugin.show(
          notification.hashCode,
          notification.title,
          notification.body,
          NotificationDetails(
            android: AndroidNotificationDetails(
              channel.id,
              channel.name,
              // TODO add a proper drawable resource to android, for now using
              //      one that already exists in example app.
              icon: 'launch_background',
            ),
          ),
        );
      }
    });
  }

在页面初始化时,您需要运行loadFCM、listenFCM和requestPermission。

  void initState() {
    super.initState();
    requestPermission();
    loadFCM();
    listenFCM();
  }

过时的方法

此代码已被弃用,并存在安全问题,因为它使用了一个API密钥与应用程序一起使用,该密钥可以被反向工程化以使用您的API密钥发送通知作为您的应用程序。仅在测试时使用此代码,因为您不需要Firebase Cloud Functions。

下一步是找到您的Firebase Cloud Messaging API密钥。这可以通过前往Firebase项目>项目设置>云消息传递来完成,然后在Cloud Messaging API(Legacy)下复制API密钥即可。

当您拥有Firebase Cloud Messaging API密钥时,这是显示通知的代码,给定通知标题、正文和设备令牌以将其发送到。

  void sendPushMessage(String body, String title, String token) async {
    try {
      await http.post(
        Uri.parse('https://fcm.googleapis.com/fcm/send'),
        headers: <String, String>{
          'Content-Type': 'application/json',
          'Authorization':
              'key=REPLACETHISWITHYOURAPIKEY',
        },
        body: jsonEncode(
          <String, dynamic>{
            'notification': <String, dynamic>{
              'body': body,
              'title': title,
            },
            'priority': 'high',
            'data': <String, dynamic>{
              'click_action': 'FLUTTER_NOTIFICATION_CLICK',
              'id': '1',
              'status': 'done'
            },
            "to": token,
          },
        ),
      );
      print('done');
    } catch (e) {
      print("error push notification");
    }
  }

现在你可以像这样调用这个函数:

sendPushMessage('通知正文', '通知标题', '替换为设备令牌');

希望这能帮到你。


2
嗨,感谢您宝贵的评论。我想问一个问题,这个解决方案有安全问题,对吧?如果您从客户端发送API密钥,它可能会被捕获,所以在服务器端使用触发器是更好的方法。但是,确实这样做比较容易,但也许不是最佳方法。你的看法是什么,@Mohammad Abd-elmoniem? - rkris26
1
我在几个应用程序和教程中看到过这种用法。对于通知方面没有太多经验,希望有人能看看这是否是一个合法的安全问题。感谢您提出这个问题! - Momoniem
大家好,我刚看到这个帖子,因为我也在尝试实现推送通知。我绝对不会将我的密钥存储在应用程序中。它可以被反向工程并用于向您的用户发送通知。 - cipano
你如何反向工程一个Flutter应用程序?@cipano - Balaji
大家好!我使用云函数添加了相关代码来解决这些安全问题。 - Momoniem
显示剩余2条评论

3

您需要使用Firebase云消息传递(Firebase Cloud Messaging)。

我所采用的方法是使用Cloud Function,您可以通过HTTP甚至Firestore触发器来触发它,如下所示:


// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

/**
 * Triggered by a change to a Firestore document.
 *
 * @param {!Object} event Event payload.
 * @param {!Object} context Metadata for the event.
 */
exports.messageNotificationTrigger = (change, context) => {
 
  db.collection('users').get().then((snapshot) => {
    snapshot.docs.forEach(doc => {
      
      const userData = doc.data();
      
      if (userData.id == '<YOUR_USER_ID>') {
         admin.messaging().sendToDevice(userData.deviceToken, {
           notification: { 
             title: 'Notification title', body: 'Notification Body'}
           });
      }
    });
  });
};

在您的用户集合中注册的每个用户都必须具有从其访问应用程序的设备发送的设备令牌。

从Flutter,使用FCM包,这是将设备令牌发送到Firebase的方法:

// fetch the device token from the Firebase Messaging instance
      // and store it securely on Firebase associated with this user uid
      FirebaseMessaging.instance.getToken().then((token) {
        FirebaseFirestore.instance.collection('users').doc(userCreds.user!.uid).set({
          'deviceToken': token
        });
      });

userCredentials.user!.uid 是使用 Firebase Authentication 登录应用程序时使用的用户,如下所示:

UserCredential userCreds = await FirebaseAuth.instance.signInWithCredential(credential);

希望能帮到你。

我感谢您的回复,但这如何让我向特定用户发送通知而不是消息呢? - Momoniem
更新了我的答案。你想要使用Firebase Cloud Messaging;我建议使用Firebase身份验证来保护你的应用程序,通过认证你的用户并使用Firebase Auth给出的唯一UID来为每个用户创建一个唯一的文档,也可以关联设备令牌,在进行Firebase Cloud Messaging时给出,并使用该令牌向特定用户发送消息。这就是我所做的,对我来说完美运作。 - Roman Jaquez
谢谢,我在哪里找到用户ID? - Momoniem
哦,我知道了。我的大脑可能有一瞬间混淆了。谢谢! - Momoniem
1
云函数需要采用“按使用量付费”的计划,只要不超过每月 2M 次调用,就是免费的。当然,你可以不使用 Google Cloud 函数,但可能需要自己管理一些事情。 - Roman Jaquez
显示剩余4条评论

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