在 Flutter 插件的 IOS 组件中,在何处以及如何包含文件资源?

Where and How to Include File Resources Within IOS component of Flutter Plugin?

假设您正在 Swift.

中编写 Flutter 插件的 IOS 组件

您的 MyFlutterPlugin.swift 文件已在 XCode 中打开。

所以我们知道我们在谈论同一件事,文件看起来像这样:

public class MyFlutterPlugin: NSObject, FlutterPlugin {

    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "my_flutter_plugin", binaryMessenger: registrar.messenger())
        let instance = SwiftMetronomeFlutterPlugin()
        registrar.addMethodCallDelegate(instance, channel: channel)
    }

    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {

    // ...

    }

}

此文件存在于磁盘上:

[your_plugin_project_name]/ios/classes/MyFlutterPlugin.swift

如何添加资源,例如音频 (WAV) 文件,以便在 MyFlutterPlugin.swift 中引用它?

在普通(非 Flutter)IOS 应用程序中,我只需将 WAV 文件拖到 XCode 与 ViewController.swift 并排的位置(在同一文件夹中)即可完成此操作,并从 ViewController 中引用它,如下所示:

let clack_Path = Bundle.main.path(forResource: "clack", ofType: "wav")
let clack_URL = URL(fileURLWithPath: clack_Path!)

但是,如果我在 MyFlutterPlugin.swift 中尝试相同的方法(将音频文件放在 MyFlutterPlugin.swift 旁边),我会得到一个错误,指出 clack_Path 为空。

那么放置文件的正确位置在哪里,以及如何正确引用它?

我找不到关于这个特定问题的任何代码示例、教程或文档(至少没有我认为有用的)。希望它实际上可以在 IOS 上做...我已经包含了资源并在插件的 Android 端工作。

试试这个:https://docs.flutter.dev/development/ui/assets-and-images#loading-flutter-assets-in-ios

简而言之,首先,将您的文件作为普通 Flutter 资产(而不是 ios/android 资产)。然后,使用link中的方法访问它。

为了能够在Flutter Plugin中使用assets,首先,我们需要在plugins的文件夹中添加资源,在这个例子中,我将clap.wav文件放在文件夹[your_plugin_project_name]/ios/Assets/.

在插件文件夹中添加文件后,we need to specify in the plugin's PodSpec where the assets are located

# File [your_plugin_project_name]/my_flutter_plugin.podspec
Pod::Spec.new do |s|
  # [...] supressed content above
  s.source_files = 'Classes/**/*'
  s.resources    = ['Assets/**.*']
  # [...] supressed content below
end

上面代码片段中的重要部分是 s.resources = ['Assets/**.*'] 行,其中 Assets/**/* 指定插件的所有资源都在文件夹 Assets 中。 Assets/**/* 是一个通配符,用于指示文件夹 Assets 中每个 folder/subfolder(** 部分)中的每个文件(* 字符串部分)必须作为插件包的资源包含在内。您可以通过搜索 regular expression.

了解更多有关此类字符串的信息

每次更改插件的配置时,都需要通知使用该插件的Flutter项目有新的更改。在常规项目中,您需要提升插件的版本并在 Pub 中发布它的新版本,但由于我们只是在本地更改它(不向世界发布),我们只需要通知Flutter 插件的文件已经过时了。最简单的方法是在使用插件的项目中 运行 flutter clean

之后,您将能够使用以下代码访问插件的 swift 文件中的 clap.wav 文件:

guard let url = Bundle(for: type(of: self)).url(forResource: "clap", withExtension: "wav") else {
   return print("File not found") 
}

请注意,您必须使用 Bundle(for: type(of: self)) 而不是 Bundle.mainBundle.main 指向 Runner 的包而不是您的插件的包)。

在下面的示例中,我展示了如何使用 swift 代码播放 clap.wav

import Flutter
import UIKit
import AVFoundation

public class SwiftMyFlutterPlugin: NSObject, FlutterPlugin {
  private var player: AVAudioPlayer?

  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "my_flutter_plugin", binaryMessenger: registrar.messenger())
    let instance = SwiftMyFlutterPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
    case "getPlatformVersion":
      return result("iOS " + UIDevice.current.systemVersion)
    case "playSound":
      return playSound(result: result)
    default:
      return result(FlutterMethodNotImplemented)
    }
  }

  private func playSound(result: @escaping FlutterResult) {
    if player?.isPlaying == true { return }

    let filename = "clap"
    let fileExtension = "wav"
    guard let url = Bundle(for: type(of: self)).url(forResource: filename, withExtension: fileExtension) else {
      let flutterError = FlutterError(
        code: "fileNotFound",
        message: "File not found: \(filename).\(fileExtension).",
        details: nil
      )
      return result(flutterError)
    }

    do {
      try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
      try AVAudioSession.sharedInstance().setActive(true)
    } catch {
      let flutterError = FlutterError(
        code: "audioSessionSetupError",
        message: "Error on AVAudionSession setup.",
        details: error.localizedDescription
      )
      return result(flutterError)
    }

    do {
      player = try AVAudioPlayer(contentsOf: url)
      player?.play()
    } catch {
      let flutterError = FlutterError(
        code: "audioPlayerSetupError",
        message: "Error on AVAudioPlayer setup.",
        details: error.localizedDescription
      )
      return result(flutterError)
    }
  }
}

上面的例子是一个简单的例子,说明了如何播放 clap.wav 文件,但你可以用其他不同的方式来播放。由于这与问题无关,我认为上面的例子足以回答你的问题。

为了便于您理解并轻松证明上面的代码有效,我在 GitHub 中创建了一个简单的项目,您可以执行该项目以查看其是否按预期工作,see it here

请注意,您必须 运行 文件夹 example.

中的代码