Flutter实现点击通知直接打开文件而不启动应用
解决方案:点击Flutter通知直接打开文件(不启动应用)
由于Flutter框架本身的限制,直接通过flutter_local_notifications实现点击通知不启动应用就打开文件需要依赖原生平台的定制逻辑,以下分Android和iOS平台分别说明实现方案:
Android 端实现
Android可以通过自定义广播接收器(BroadcastReceiver)处理通知点击事件,直接调用系统打开文件的Intent,跳过应用启动流程:
- 创建BroadcastReceiver
在Android项目的android/app/src/main/kotlin/[你的包名]/目录下新建NotificationReceiver.kt文件,代码如下:
import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.net.Uri import java.io.File class NotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { intent?.getStringExtra("file_path")?.let { filePath -> val targetFile = File(filePath) if (targetFile.exists()) { val openFileIntent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(Uri.fromFile(targetFile), getFileMimeType(filePath)) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION } context?.startActivity(openFileIntent) } } } // 简易MIME类型判断,可根据业务需求扩展支持的文件类型 private fun getFileMimeType(filePath: String): String { return when { filePath.endsWith(".pdf", ignoreCase = true) -> "application/pdf" filePath.endsWith(".jpg", ignoreCase = true) || filePath.endsWith(".jpeg", ignoreCase = true) -> "image/jpeg" filePath.endsWith(".png", ignoreCase = true) -> "image/png" filePath.endsWith(".txt", ignoreCase = true) -> "text/plain" else -> "*/*" } } }
- 注册BroadcastReceiver
在AndroidManifest.xml的<application>标签内添加接收器注册:
<receiver android:name=".NotificationReceiver" android:exported="true"> <intent-filter> <!-- 自定义Action,需与Flutter端保持一致 --> <action android:name="com.your.package.action.OPEN_FILE" /> </intent-filter> </receiver>
注意替换com.your.package为你的应用实际包名。
- Flutter端配置通知
创建通知时,将AndroidNotificationDetails的intent设置为指向上述广播接收器的PendingIntent:
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter/services.dart'; final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // 初始化通知(省略常规初始化代码) // 创建通知的方法 Future<void> showFileNotification(String filePath) async { const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'file_notification_channel', '文件通知频道', channelDescription: '用于触发文件打开的通知频道', importance: Importance.high, priority: Priority.high, // 配置PendingIntent指向自定义广播 intent: PendingIntent.getBroadcast( await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()!.getApplicationContext(), 0, Intent("com.your.package.action.OPEN_FILE") ..putExtra("file_path", filePath), PendingIntentFlags.updateCurrent | PendingIntentFlags.immutable, ), ); const NotificationDetails notificationDetails = NotificationDetails(android: androidDetails); await flutterLocalNotificationsPlugin.show( 0, "文件通知", "点击打开目标文件", notificationDetails, ); }
iOS 端实现
iOS系统限制了通知点击后的直接后台操作,无法完全避免应用启动,但可以通过优化让应用在后台打开文件,用户几乎感知不到应用启动:
配置后台权限
打开Xcode,进入项目的Signing & Capabilities页面,添加Background Modes权限并勾选Remote notifications(本地通知也依赖此权限实现后台处理)。修改AppDelegate处理通知点击
在ios/Runner/AppDelegate.swift中修改代码,实现通知点击后的文件打开逻辑,并在打开后让应用进入后台:
import UIKit import Flutter import UserNotifications @UIApplicationMain class AppDelegate: FlutterAppDelegate, UNUserNotificationCenterDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { UNUserNotificationCenter.current().delegate = self GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // 从通知的userInfo中获取文件路径 if let filePath = response.notification.request.content.userInfo["file_path"] as? String { let fileURL = URL(fileURLWithPath: filePath) // 调用系统API打开文件 UIApplication.shared.open(fileURL, options: [:]) { success in // 文件打开后让应用进入后台 if let window = self.window { window.isHidden = true } UIApplication.shared.perform(#selector(NSApplication.shared.hide(nil))) } } completionHandler() } }
- Flutter端配置iOS通知
创建通知时,在IOSNotificationDetails的userInfo中传入文件路径:
Future<void> showFileNotification(String filePath) async { const IOSNotificationDetails iosDetails = IOSNotificationDetails( userInfo: {"file_path": filePath}, ); const NotificationDetails notificationDetails = NotificationDetails(ios: iosDetails); await flutterLocalNotificationsPlugin.show( 0, "文件通知", "点击打开目标文件", notificationDetails, ); }
注意事项
- Android平台:确保文件路径可访问,Android 13及以上版本需要申请对应媒体权限(如
READ_MEDIA_IMAGES),外部存储文件需处理权限适配。 - iOS平台:文件必须存储在应用沙盒内或应用有权限访问的路径(如iCloud共享文件),否则无法打开。
- 测试时需完全关闭应用(从多任务列表划掉),再触发通知验证效果。
内容的提问来源于stack exchange,提问作者Philipos D.
相关产品推荐
相关产品推荐

