Flutter Android应用终止/关闭时FCM推送通知失效求助
Flutter Android应用FCM推送问题:终止/关闭状态下通知失效,需登录后才生效
我开发的Flutter Android应用使用Firebase数据库,通过FCM实现推送通知。核心问题是:应用处于终止或关闭状态时,通知无法正常接收,必须完成登录操作后通知才会生效。已查阅YouTube教程及官方文档,问题仍未解决,相关代码如下:
main.dart 代码
// main.dart import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'login_screen.dart'; import 'notification_service.dart'; final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); final NotificationService notificationService = NotificationService(); Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); // 确保后台中Firebase已初始化 notificationService.showNotification( message.data['title'], message.data['body'], ); print("Handling a background message: ${message.messageId}"); } Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 注册后台消息处理器 FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); // 初始化本地通知 initializeLocalNotifications(); // 为通知进行登录操作 await _signInForNotifications(); // 获取FCM令牌并保存到Firebase await _initializeFCMToken(); runApp(const FireGuard()); } class FireGuard extends StatelessWidget { const FireGuard({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'FireGuard Fire Safety', theme: ThemeData( primarySwatch: Colors.red, ), home: const LoginScreen(), ); } } Future<void> _signInForNotifications() async { try { await FirebaseAuth.instance.signInWithEmailAndPassword( email: "", password: "", ); print("已登录以接收通知。"); } catch (e) { print("登录失败,无法接收通知: $e"); } } Future<void> _initializeFCMToken() async { // 请求iOS通知权限 FirebaseMessaging messaging = FirebaseMessaging.instance; await messaging.requestPermission(); // 获取FCM令牌 String? token = await messaging.getToken(); if (token != null) { await _saveTokenToNotificationNode(token); } // 监听令牌刷新,更新保存的令牌 FirebaseMessaging.instance.onTokenRefresh.listen(_saveTokenToNotificationNode); } Future<void> _saveTokenToNotificationNode(String token) async { final DatabaseReference notificationRef = FirebaseDatabase.instance.ref().child("notifications"); // 将FCM令牌直接作为键保存到notifications节点 await notificationRef.child(token).set({ "FCMToken": token, "isActive": true, // 可选字段,标记令牌状态 }); print("令牌已保存到Firebase的notifications节点: $token"); } void initializeLocalNotifications() async { const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid); await flutterLocalNotificationsPlugin.initialize(initializationSettings); }
notification_service.dart 代码
// notification_service.dart import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:vibration/vibration.dart'; import 'main.dart'; // 导入flutterLocalNotificationsPlugin class NotificationService { final FirebaseMessaging _messaging = FirebaseMessaging.instance; NotificationService() { _initializeFCM(); } // 初始化FCM void _initializeFCM() async { // 请求通知权限并订阅主题 NotificationSettings settings = await _messaging.requestPermission( alert: true, announcement: true, badge: true, carPlay: false, criticalAlert: true, provisional: false, sound: true, ); print('用户授权状态: ${settings.authorizationStatus}'); await _messaging.subscribeToTopic('fire_alerts'); // FirebaseMessaging.onMessage.listen((RemoteMessage message) { // if (message.notification != null) { // showNotification( // message.data['title'] ?? '通知', // message.data['body'] ?? '您有一条新消息。', // ); // } // }); // 处理后台消息点击 FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { // _handleNotificationClick(message.data); showNotification( message.data['title'] ?? '通知', message.data['body'] ?? '您有一条新消息。', ); }); } // 显示通知的公共方法 void showNotification(String title, String body, {bool vibrate = false}) async { const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails( 'your_channel_id', '您的频道名称', channelDescription: '您的频道描述', importance: Importance.max, priority: Priority.high, showWhen: true, ); const NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics); await flutterLocalNotificationsPlugin.show( 0, title, body, platformChannelSpecifics, ); // 如果支持振动且开启振动选项,则触发振动 if (vibrate && (await Vibration.hasVibrator() ?? false)) { Vibration.vibrate(duration: 500); // 振动500毫秒 } } // 将FCM令牌保存到Firebase(可根据需要自定义) Future<void> _saveTokenToFirebase(String token) async { print("FCM令牌已保存: $token"); } // 发送系统通知的公共方法 void sendSystemNotification(String title, String body, {bool vibrate = true}) { showNotification(title, body, vibrate: vibrate); } }
DashboardNotification 相关代码
import 'package:fireguard/notification_service.dart'; import 'package:flutter/material.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:intl/intl.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart'; import 'package:vibration/vibration.dart'; class DashboardNotification extends StatefulWidget { final String username; const DashboardNotification({super.key, required this.username}); @override _DashboardNotificationState createState() => _DashboardNotificationState(); } class _DashboardNotificationState extends State<DashboardNotification> { String? macAddress; DateTime? loginTime; final DatabaseReference _clientAccountsRef = FirebaseDatabase.instance.ref().child("Client Accounts"); final DatabaseReference _devicesRef = FirebaseDatabase.instance.ref().child("devices"); final NotificationService _notificationService = NotificationService(); List<Map<String, dynamic>> _fireNotifications = []; final Map<String, String> _lastNotifiedTimestamp = {}; @override void initState() { super.initState(); loginTime = DateTime.now(); // _initializeFCM(); // 初始化FCM以接收通知 _loadUserDevicesAndFireHistory(); //initializeNotifications(); } Future<void> _saveLastNotifiedTimestamps() async { SharedPreferences prefs = await SharedPreferences.getInstance(); List<String> timestamps = _lastNotifiedTimestamp.entries .map((entry) => "${entry.key}:${entry.value}") .toList(); await prefs.setStringList('lastNotifiedTimestamps', timestamps); } void _sendSystemNotification(String macAddress, String fireTimestamp, String severityLevel) { final DateTime now = DateTime.now(); final DateTime fireEventTime = DateFormat('MM/dd/yyyy hh:mm:ss a').parse(fireTimestamp); // 检查火灾事件是否发生在登录之后,避免旧事件触发通知 if (fireEventTime.isAfter(loginTime!)) { final DateTime? lastNotified = _lastNotifiedTimestamp[macAddress] != null ? DateFormat('MM/dd/yyyy hh:mm:ss a').parse(_lastNotifiedTimestamp[macAddress]!) : null; // 设置冷却时间(5分钟) const int cooldownPeriodInSeconds = 300; if (lastNotified == null || now.difference(lastNotified).inSeconds > cooldownPeriodInSeconds) { // 使用NotificationService发送带振动的通知 _notificationService.sendSystemNotification( '检测到火灾! - 严重等级: $severityLevel', '设备$macAddress于$fireTimestamp检测到火灾 ', vibrate: true, ); // 更新设备的最后通知时间戳 _lastNotifiedTimestamp[macAddress] = DateFormat('MM/dd/yyyy hh:mm:ss a').format(now); // 将最后通知时间戳保存到本地存储 _saveLastNotifiedTimestamps(); } else { print('通知已抑制,避免重复发送(冷却生效中)'); } } else { print('通知已抑制,火灾事件发生在登录之前。'); } }
问题排查与解决方案
1. 后台消息处理依赖登录状态
应用终止/关闭时,后台消息处理器不会执行main函数中的_signInForNotifications()登录逻辑,导致Firebase操作可能失败,无法触发通知。
修复:
在后台处理器中添加匿名登录(需确保Firebase规则允许匿名访问):
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); // 后台环境使用匿名登录,避免Auth状态缺失 try { await FirebaseAuth.instance.signInAnonymously(); } catch (e) { print("匿名登录失败: $e"); } // 直接初始化本地通知,不依赖全局实例 const AndroidInitializationSettings initSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initSettings = InitializationSettings(android: initSettingsAndroid); FlutterLocalNotificationsPlugin localPlugin = FlutterLocalNotificationsPlugin(); await localPlugin.initialize(initSettings); const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'your_channel_id', '您的频道名称', description: '您的频道描述', importance: Importance.max, priority: Priority.high, ); await localPlugin.show( 0, message.data['title'], message.data['body'], NotificationDetails(android: androidDetails), ); print("处理后台消息: ${message.messageId}"); }
2. 令牌保存与登录解耦
当前令牌保存依赖登录状态,若登录失败则令牌无法同步到数据库,服务器无法推送。
修复:
修改_initializeFCMToken(),移除对登录的依赖,确保无论登录状态如何都能保存令牌:
Future<void> _initializeFCMToken() async { FirebaseMessaging messaging = FirebaseMessaging.instance; await messaging.requestPermission(); String? token = await messaging.getToken(); if (token != null) { await _saveTokenToNotificationNode(token); } FirebaseMessaging.instance.onTokenRefresh.listen(_saveTokenToNotificationNode); }
3. 补全AndroidManifest配置
确保AndroidManifest中添加FCM所需权限和服务:
<!-- 添加权限 --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 添加FCM服务 --> <service android:name="com.google.firebase.messaging.FirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
4. 创建Android通知渠道
Android 8.0+需显式创建通知渠道,否则后台通知无法显示:
void initializeLocalNotifications() async { const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid); // 创建通知渠道 AndroidNotificationChannel channel = const AndroidNotificationChannel( 'your_channel_id', '您的频道名称', description: '您的频道描述', importance: Importance.max, ); await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel); await flutterLocalNotificationsPlugin.initialize(initializationSettings); }
内容的提问来源于stack exchange,提问作者L. Dayrit
相关产品推荐
相关产品推荐

