You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现点击通知直接打开文件而不启动应用

解决方案:点击Flutter通知直接打开文件(不启动应用)

由于Flutter框架本身的限制,直接通过flutter_local_notifications实现点击通知不启动应用就打开文件需要依赖原生平台的定制逻辑,以下分Android和iOS平台分别说明实现方案:


Android 端实现

Android可以通过自定义广播接收器(BroadcastReceiver)处理通知点击事件,直接调用系统打开文件的Intent,跳过应用启动流程:

  1. 创建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 -> "*/*"
        }
    }
}
  1. 注册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为你的应用实际包名。

  1. 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系统限制了通知点击后的直接后台操作,无法完全避免应用启动,但可以通过优化让应用在后台打开文件,用户几乎感知不到应用启动:

  1. 配置后台权限
    打开Xcode,进入项目的Signing & Capabilities页面,添加Background Modes权限并勾选Remote notifications(本地通知也依赖此权限实现后台处理)。

  2. 修改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()
    }
}
  1. 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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 14:01:30