Android14下flutter_local_notifications实现不可滑动删除的永久通知
解决Android 14中Flutter永久通知可滑动删除的问题
核心问题分析
在Android 14中,仅设置ongoing: true不足以阻止用户滑动删除通知,系统对通知的可清除逻辑做了调整:
- 普通的
ongoing通知仍允许用户滑动删除,除非该通知属于前台服务绑定的通知,或指定了特定服务类别。 - 你尝试的Workmanager周期性任务会重复创建新通知(
taskNotificationId自增),旧通知会被系统视为可清除;且周期性任务本身不是持续的前台服务,无法维持永久通知。
修复方案
1. 使用前台服务绑定通知(真正实现不可滑动删除)
Android中只有前台服务的通知才会被系统标记为不可滑动删除,结合flutter_local_notifications实现步骤:
调整通知配置
在AndroidNotificationDetails中添加关键参数,强化不可清除属性:
const AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails( 'task-channel', 'Task Notification', channelDescription: 'Permanent Task Notification reminder', importance: Importance.max, priority: Priority.high, ongoing: true, autoCancel: false, // 指定服务类别,通知系统这是后台服务关联通知 category: AndroidNotificationCategory.service, // 禁用灯光、震动,避免触发额外交互逻辑 enableLights: false, enableVibration: false, // 标记为前台服务通知类型 foregroundServiceType: AndroidForegroundServiceType.regular, );
启动前台服务绑定通知
通过flutter_local_notifications的原生方法启动前台服务,将通知与服务绑定:
// 在initialize完成后调用,启动前台服务并显示永久通知 Future<void> startPermanentNotification({ required String title, required String description, }) async { await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.startForeground( 1, // 固定通知ID,避免重复创建 title, description, androidNotificationDetails); }
2. 修复Workmanager版本的重复通知问题
如果需要后台任务配合,不要用周期性任务重复创建通知,而是维持同一个通知的存活:
- 移除
taskNotificationId自增逻辑,始终使用同一个ID显示通知:
await flutterLocalNotificationsPlugin.show( 1, // 固定ID,覆盖旧通知而非创建新通知 'Background Service', 'Service is running', platformChannelSpecifics, );
3. 解决cleanUp()的Failure错误
错误原因是调用disableBackgroundExecution后,前台服务与通知的绑定未正确解除,调整清理逻辑:
Future<void> cleanUp() async { // 先停止前台服务并移除关联通知 await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.stopForeground(true); // true表示同时移除通知 await Workmanager().cancelAll(); await flutterLocalNotificationsPlugin.cancelAll(); await FlutterBackground.disableBackgroundExecution(); }
完整调整后的示例代码
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; class NotificationProvider { late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; static const int permanentNotificationId = 1; // 固定通知ID NotificationProvider() { flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); } void _onDidReceiveNotificationResponse( NotificationResponse notificationResponse) async { final String? payload = notificationResponse.payload; if (payload != null) { print('notification payload: $payload'); } } void _onDidReceiveLocalNotification( int id, String? title, String? body, String? payload) async { print("id $id, title $title, body $body, payload $payload"); } Future<void> initialize() async { const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('app_icon'); final DarwinInitializationSettings initializationSettingsDarwin = DarwinInitializationSettings( onDidReceiveLocalNotification: _onDidReceiveLocalNotification); final LinuxInitializationSettings initializationSettingsLinux = LinuxInitializationSettings(defaultActionName: 'Open notification'); final InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsDarwin, macOS: initializationSettingsDarwin, linux: initializationSettingsLinux, ); await flutterLocalNotificationsPlugin.initialize( initializationSettings, onDidReceiveNotificationResponse: _onDidReceiveNotificationResponse, ); // 创建匹配的通知渠道,确保渠道属性与通知一致 const AndroidNotificationChannel channel = AndroidNotificationChannel( 'task-channel', 'Task Notification', description: 'Permanent Task Notification reminder', importance: Importance.max, enableVibration: false, enableLights: false, ); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>()! .requestNotificationsPermission(); } Future<void> startPermanentNotification({ required String title, required String description, }) async { const AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails( 'task-channel', 'Task Notification', channelDescription: 'Notification reminder for Tasks', importance: Importance.max, priority: Priority.high, ongoing: true, autoCancel: false, category: AndroidNotificationCategory.service, enableLights: false, enableVibration: false, foregroundServiceType: AndroidForegroundServiceType.regular, ); const NotificationDetails notificationDetails = NotificationDetails(android: androidNotificationDetails); // 启动前台服务并绑定通知 await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.startForeground( permanentNotificationId, title, description, notificationDetails); } Future<void> cleanUp() async { // 先停止前台服务并移除通知 await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.stopForeground(true); await flutterLocalNotificationsPlugin.cancelAll(); } }
关键注意事项
- 在
AndroidManifest.xml中添加必要权限:<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> - 不要依赖Workmanager周期性任务维持通知,前台服务是唯一能保证通知永久不可删除的方式。
- 确保通知渠道的配置与通知详情一致,渠道属性会直接影响通知的可清除性。
内容的提问来源于stack exchange,提问作者André
相关产品推荐
相关产品推荐

