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

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适配缺失导致的,按以下步骤调整即可解决:

  1. 替换插件版本、调整初始化时机
    建议替换为维护更活跃的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());
}
  1. 补全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)
    }
  }
}
  1. 权限注意事项
    iOS端返回的外部文件路径属于安全域路径,插件内部已经自动完成了startAccessingSecurityScopedResource权限申请,拿到路径后不要手动拼接修改路径,直接使用返回的path即可正常读取文件内容。

内容的提问来源于stack exchange,提问作者Jasur Jaxongirov Davron õğli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 09:39:57