Flutter FCM Android端无悬浮通知无提示音问题求助
问题现象
集成FCM实现通知收发功能时,Android端设备(模拟器/实体机)收到通知后既不展示Heads Up悬浮通知,也不播放提示音。已在AndroidNotificationDetails中配置importance: Importance.max、priority: Priority.high、playSound: true参数仍未生效。
相关配置截图
- 应用安装后通知设置截图:

- 通知渠道配置截图:

现有实现代码
FCM初始化代码
static Future initFireBase() async { await Firebase.initializeApp(); NotificationSettings settings = await FirebaseMessaging.instance.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'); } FirebaseMessaging.onBackgroundMessage(backgroundHandler); var token = await FirebaseMessaging.instance.getToken(); log('Token $token', name: _className); FirebaseMessaging.instance.onTokenRefresh.listen((newToken) { log('New Token $newToken', name: _className); }); // 点击终止状态下的通知启动应用逻辑 await FirebaseMessaging.instance.getInitialMessage().then((message) async { if (message != null) { await LocalNotificationHandler.displayNotification(message); message.data.forEach((key, value) { }); } }); // 前台通知监听 FirebaseMessaging.onMessage.listen((message) { log('foreground onMessage', name: _className); if (message.notification != null) { log('title ${message.notification!.title}', name: _className); log('body ${message.notification!.body}', name: _className); } LocalNotificationHandler.displayNotification(message); }); // 应用在后台存活状态下的通知点击监听 FirebaseMessaging.onMessageOpenedApp.listen((message) { log('Background onMessageOpenedApp', name: _className); log('data ${message.data.toString()}', name: _className); });
已知逻辑说明:
requestPermission方法仅对iOS平台生效,Android端会默认返回授权成功状态,不会弹出权限申请弹窗,会直接打印User granted permission日志。
本地通知实现代码
class LocalNotificationHandler { static const String _className = 'Local Notification Handler'; static final FlutterLocalNotificationsPlugin _localNotificationsPlugin = FlutterLocalNotificationsPlugin(); static void initialize() { const InitializationSettings initializationSettings = InitializationSettings( android: AndroidInitializationSettings('@mipmap/ic_launcher'), ); _localNotificationsPlugin.initialize(initializationSettings,onSelectNotification: (String? data) async{ if(data != null){ log('_localNotificationsPlugin data $data', name: _className); } }); } static Future<void> displayNotification(RemoteMessage message) async { try { final id = DateTime.now().millisecondsSinceEpoch ~/ 1000; NotificationDetails notificationDetails = const NotificationDetails( android: AndroidNotificationDetails( 'realestateid', //渠道ID 'realestate channel', //渠道名称 importance: Importance.max, priority: Priority.high, playSound: true, ), iOS: IOSNotificationDetails( presentAlert: true, presentBadge: true, presentSound: true, ) ); await _localNotificationsPlugin.show(id, message.notification!.title, message.notification!.body, notificationDetails,payload: message.data['route']); } on Exception catch (e) { log('display Notification Exception $e', name: _className); } } }
入口初始化代码
// 应用终止状态下的通知后台处理顶层函数 await LocalNotificationHandler.displayNotification(message); void main()async { WidgetsFlutterBinding.ensureInitialized(); // 初始化FCM相关通知 LocalNotificationHandler.initialize(); await FCMNotification.initFireBase(); }
修复方案
1. 补充Android 13+ 通知动态权限申请
Android 13(API 33)及以上版本需要单独申请POST_NOTIFICATIONS运行时权限,firebase_messaging的requestPermission不会自动处理该权限,需要手动补充:
- 首先在
android/app/src/main/AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
- 在本地通知初始化逻辑中补充权限申请代码:
static void initialize() async { const InitializationSettings initializationSettings = InitializationSettings( android: AndroidInitializationSettings('@mipmap/ic_launcher'), ); _localNotificationsPlugin.initialize(initializationSettings,onSelectNotification: (String? data) async{ if(data != null){ log('_localNotificationsPlugin data $data', name: _className); } }); // 新增Android 13+通知权限申请 if (Platform.isAndroid) { final AndroidFlutterLocalNotificationsPlugin? androidImpl = _localNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>(); await androidImpl?.requestNotificationsPermission(); } }
2. 修复通知渠道配置不生效问题
Android通知渠道一旦创建,后续通过AndroidNotificationDetails传入的渠道参数不会覆盖已有渠道的配置,你代码中设置的高优先级、声音参数如果是在渠道创建后才修改的,完全不会生效。
修复方式二选一即可:
- 直接卸载测试设备上的旧应用,重新安装运行,让新的渠道配置生效;
- 修改原有渠道ID(比如把
realestateid改成realestateid_v2),强制应用创建新的通知渠道,同时建议在初始化时主动创建渠道,避免配置不统一:
// 在权限申请代码后补充渠道创建逻辑 const AndroidNotificationChannel highChannel = AndroidNotificationChannel( 'realestateid_v2', 'realestate channel', importance: Importance.max, priority: Priority.high, playSound: true, enableVibration: true, ); await androidImpl?.createNotificationChannel(highChannel);
后续发通知时AndroidNotificationDetails中传入和创建时一致的渠道ID即可。
3. 校验系统级通知开关
部分国产定制ROM(小米、华为、OPPO等)默认会关闭第三方应用的悬浮通知权限,需要手动进入应用通知设置页,确认对应通知渠道的「悬浮通知」「通知铃声」开关处于开启状态。
4. 补全离线/后台FCM推送参数
如果是应用在后台、终止状态下收到通知没有悬浮弹窗和声音,需要确认服务端推送的payload配置正确:
- 通知优先级设置为
high - 明确指定
sound参数为默认铃声 android_channel_id和你本地创建的高优先级渠道ID完全一致
示例payload配置:
{ "notification": { "title": "通知标题", "body": "通知内容", "sound": "default" }, "android": { "priority": "high", "notification": { "channel_id": "realestateid_v2", "sound": "default", "priority": "high", "default_sound": true } }, "data": { "route": "/home" } }
内容的提问来源于stack exchange,提问作者Abanoub Refaat

