Flutter应用如何打开外部文件并解决iOS端文件接收问题
Flutter 实现外部文件打开/关联功能方案
实现目标:
- 点击App内图片类文件时,弹出系统打开方式选择菜单,可选择系统图库或其他图片查看应用打开
- 自研App注册到PDF等指定类型文件的系统启动应用列表,用户选择文件打开方式时可展示该应用,效果参考:

一、App内唤起系统打开文件菜单
使用open_filex插件即可快速实现全平台适配,无需编写大量原生代码:
- 引入依赖后,直接传入文件本地绝对路径即可唤起系统选择弹窗,支持自动匹配对应文件类型的可用应用
- 核心调用代码:
import 'package:open_filex/open_filex.dart'; // 打开文件,可手动传入MIME类型做精准匹配 await OpenFilex.open( localFileAbsolutePath, type: 'image/*', // 图片类型传image/*,PDF传application/pdf );
- 若需要自定义跳转逻辑(比如指定跳转某款应用),可搭配
url_launcher插件实现。
二、App注册为系统指定文件类型的打开选项
Android端通过原生配置实现文件URI转绝对路径的方案已经成熟,以下重点说明iOS端的实现与问题排查方案,解决receive_sharing_intent系列插件接收文件URL失效的问题。
2.1 基础配置(Info.plist)
打开iOS项目下的ios/Runner/Info.plist,添加文件类型声明,配置完成后App就会出现在对应文件的打开列表中(以PDF、常见图片格式为例,可按需增删类型):
<key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>PDF 文档</string> <key>LSHandlerRank</key> <string>Alternate</string> <key>LSItemContentTypes</key> <array> <string>com.adobe.pdf</string> </array> </dict> <dict> <key>CFBundleTypeName</key> <string>图片文件</string> <key>LSHandlerRank</key> <string>Alternate</string> <key>LSItemContentTypes</key> <array> <string>public.jpeg</string> <string>public.png</string> <string>public.gif</string> </array> </dict> </array>
注意:
LSHandlerRank不要设置为Owner,除非你的App是该文件类型的默认专属处理应用,否则会导致系统文件关联异常,设置为Alternate即可正常出现在可选列表中。如果需要支持自定义后缀的文件,需要额外配置UTExportedTypeDeclarations声明自定义UTI。
2.2 接收外部文件URL的正确实现
绝大多数场景下receive_sharing_intent失效都是配置遗漏、初始化时机错误、iOS 13+ SceneDelegate适配缺失导致的,按以下步骤调整即可解决:
- 替换插件版本、调整初始化时机
建议替换为维护更活跃的receive_sharing_intent_plus版本,对高版本iOS系统适配更完善;注意必须在runApp之前完成事件监听初始化,冷启动场景下的文件传递事件如果初始化过晚会被系统直接丢弃。
正确初始化代码示例:
import 'package:flutter/material.dart'; import 'package:receive_sharing_intent_plus/receive_sharing_intent_plus.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); // 处理冷启动场景(App未运行时被文件唤起) ReceiveSharingIntentPlus.getInitialMedia().then((fileList) { if (fileList.isNotEmpty) { final targetFilePath = fileList.first.path; // 在这里编写拿到文件路径后的业务逻辑 debugPrint('冷启动接收文件路径:$targetFilePath'); } }); // 处理后台唤起场景(App在后台运行时被文件唤起) ReceiveSharingIntentPlus.getMediaStream().listen((fileList) { if (fileList.isNotEmpty) { final targetFilePath = fileList.first.path; debugPrint('后台唤起接收文件路径:$targetFilePath'); } }); runApp(const MyApp()); }
- 补全SceneDelegate回调(iOS 13+ 必做)
大部分旧教程只配置了AppDelegate的回调,遗漏了SceneDelegate的事件传递,会导致iOS 13以上版本无法接收文件事件:
打开ios/Runner/SceneDelegate.swift文件,添加如下代码:
import UIKit import Flutter import receive_sharing_intent_plus class SceneDelegate: UIResponder, UIWindowSceneDelegate { // 保留文件原有代码,新增以下两个方法中的逻辑 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // 处理冷启动文件事件 if let fileUrl = connectionOptions.urlContexts.first?.url { ReceiveSharingIntentPlusPlugin.instance.handleUrl(url: fileUrl) } guard let windowScene = scene as? UIWindowScene else { return } let window = UIWindow(windowScene: windowScene) window.rootViewController = MainViewController() self.window = window window.makeKeyAndVisible() } func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { // 处理后台唤起文件事件 if let fileUrl = URLContexts.first?.url { ReceiveSharingIntentPlusPlugin.instance.handleUrl(url: fileUrl) } } }
- 权限注意事项
iOS端返回的外部文件路径属于安全域路径,插件内部已经自动完成了startAccessingSecurityScopedResource权限申请,拿到路径后不要手动拼接修改路径,直接使用返回的path即可正常读取文件内容。
内容的提问来源于stack exchange,提问作者Jasur Jaxongirov Davron õğli
相关产品推荐
相关产品推荐

