如何用Python在Firebase通知中添加带API跳转的操作按钮?
解决FCM推送带操作按钮通知的问题
问题根源
你之前的修改把notification字段替换成了data字段,导致系统通知栏无法自动弹出通知——FCM的data类型消息需要App自行处理显示逻辑,而notification类型消息才会由系统直接展示在通知栏。同时,操作按钮的配置不符合FCM规范,需要针对Android和iOS分别适配。
一、修正Python端的FCM请求体
保留notification字段确保通知能弹出,同时添加操作按钮配置,并用data字段携带按钮对应的API地址:
import requests import json def SendCallNotification(): try: server_token = 'my server token' device_token = 'my device token' except: return False headers = { 'Content-Type': 'application/json', 'Authorization': 'key=' + server_token, } body = { "to": device_token, "priority": "high", # 通知核心内容,确保系统弹出通知 "notification": { "title": "Incoming Call", "body": "You have a new call request", "click_action": "FLUTTER_NOTIFICATION_CLICK", # Android端操作按钮配置 "actions": [ { "action": "RECEIVE_ACTION", "title": "Receive" }, { "action": "DECLINE_ACTION", "title": "Decline" } ] }, # 携带按钮对应的API地址,供Flutter端调用 "data": { "receive_api": "https://exampleapi.com/api/v1/receive", "decline_api": "https://exampleapi.com/api/v1/decline" }, # iOS端需要配置Category,对应后续Flutter的配置 "apns": { "payload": { "aps": { "category": "CALL_CATEGORY" } } } } response = requests.post("https://fcm.googleapis.com/fcm/send", headers=headers, data=json.dumps(body)) print(response.status_code) print(response.json()) SendCallNotification()
二、Flutter端配置与逻辑处理
需要结合firebase_messaging和flutter_local_notifications插件,处理通知显示和按钮点击事件:
1. 依赖配置
在pubspec.yaml中添加依赖:
dependencies: firebase_core: ^2.15.0 firebase_messaging: ^14.6.5 flutter_local_notifications: ^16.1.0 http: ^1.1.0
2. 核心逻辑实现
import 'dart:convert'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:http/http.dart' as http; final FlutterLocalNotificationsPlugin _localNotifications = FlutterLocalNotificationsPlugin(); Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); await _initializeNotifications(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold(appBar: AppBar(title: const Text('FCM Demo'))), ); } } Future<void> _initializeNotifications() async { // 请求通知权限 await FirebaseMessaging.instance.requestPermission( alert: true, badge: true, sound: true, ); // 配置Android通知渠道 const AndroidNotificationChannel androidChannel = AndroidNotificationChannel( 'call_channel', 'Call Notifications', description: 'Handles incoming call requests', importance: Importance.max, ); await _localNotifications.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(androidChannel); // 配置iOS通知Category const IOSNotificationCategory iosCategory = IOSNotificationCategory( 'CALL_CATEGORY', actions: [ IOSNotificationAction( 'RECEIVE_ACTION', 'Receive', options: IOSNotificationActionOptions.foreground, ), IOSNotificationAction( 'DECLINE_ACTION', 'Decline', options: IOSNotificationActionOptions.foreground, ), ], ); await _localNotifications.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()?.setNotificationCategories([iosCategory]); // 处理前台收到的通知(FCM前台不会自动弹出,需手动显示) FirebaseMessaging.onMessage.listen((RemoteMessage message) { _showNotification(message); }); // 处理后台/终止状态下的通知点击 FirebaseMessaging.onMessageOpenedApp.listen(_handleNotificationAction); RemoteMessage? initialMessage = await FirebaseMessaging.getInitialMessage(); if (initialMessage != null) { _handleNotificationAction(initialMessage); } // 处理本地通知按钮点击 _localNotifications.initialize( const InitializationSettings( android: AndroidInitializationSettings('@mipmap/ic_launcher'), iOS: IOSInitializationSettings(), ), onDidReceiveNotificationResponse: (NotificationResponse response) { if (response.payload != null) { Map<String, dynamic> data = jsonDecode(response.payload!); String action = response.actionId!; _callApi(action, data); } }, ); } void _showNotification(RemoteMessage message) { const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'call_channel', 'Call Notifications', channelDescription: 'Handles incoming call requests', actions: [ AndroidNotificationAction('RECEIVE_ACTION', 'Receive'), AndroidNotificationAction('DECLINE_ACTION', 'Decline'), ], ); const IOSNotificationDetails iosDetails = IOSNotificationDetails( categoryIdentifier: 'CALL_CATEGORY', ); const NotificationDetails notificationDetails = NotificationDetails( android: androidDetails, iOS: iosDetails, ); _localNotifications.show( DateTime.now().millisecond, message.notification?.title, message.notification?.body, notificationDetails, payload: jsonEncode(message.data), ); } void _handleNotificationAction(RemoteMessage message) { String? action = message.data['action']; if (action == null) return; _callApi(action, message.data); } void _callApi(String action, Map<String, dynamic> data) { String url = ''; if (action == 'RECEIVE_ACTION') { url = data['receive_api'] ?? ''; } else if (action == 'DECLINE_ACTION') { url = data['decline_api'] ?? ''; } if (url.isNotEmpty) { http.get(Uri.parse(url)).catchError((e) => print('API call failed: $e')); } }
3. AndroidManifest.xml补充配置
在android/app/src/main/AndroidManifest.xml的主Activity标签内添加:
<intent-filter> <action android:name="FLUTTER_NOTIFICATION_CLICK" /> <action android:name="RECEIVE_ACTION" /> <action android:name="DECLINE_ACTION" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
关键说明
- 必须保留
notification字段才能让系统自动弹出通知,data字段用于携带业务逻辑所需的额外信息。 - Android和iOS的操作按钮配置逻辑不同:Android在FCM payload的
notification.actions中配置,iOS则需要先定义Notification Category,再在FCM payload的apns.payload.aps.category中关联。 - 前台收到FCM通知时,系统不会自动弹出,需要用
flutter_local_notifications手动显示带按钮的本地通知。
内容的提问来源于stack exchange,提问作者Proud Wadhwa
相关产品推荐
相关产品推荐

