如何访问纯飞镖包中的资产文件?

How to access asset file in pure dart package?

原题

我写了一个 dart 包,我的 flutter 应用程序正在使用它。 在 dart 包内,我想将一些静态数据存储在 json 文件中,我想从 dart 包代码中读取该文件。

但是我找不到直接从 dart 包访问资产文件的方法。使用 File(path).readAsString() 仅适用于 dart 控制台应用程序,使用 rootBundle 仅适用于 flutter packages/applications.

我的问题是:如何直接从纯 dart 包访问存储在 dart 包资产中的这个文件?

在 flutter 包中,我会像这样 pubspec.yml 简单地使文件可用

flutter:
   assets:
      - lib/assets/b737.json

但我找不到类似的纯 dart 包解决方案。

感谢任何帮助。

回答

经过长时间的研究和社区的大力帮助,此功能似乎根本不存在。

但有解决方法:

从 Flutter 项目加载并将读取的字符串传递给 dart 包

通过rootBundle.loadString()从flutter项目中加载asset文件并在flutter中指定asset文件pubspec.yaml并将字符串数据传递给dart包。检查

让它成为一个flutter包

一个简单的解决方案是将纯 dart 包转换为 flutter 包。然后它不再是纯粹的飞镖,但这并不总是有害的。特别是,当包在另一个 flutter 中使用时 project/package.

将原始数据保存在变量中

除了提供资产文件并在运行时读取内容,您还可以直接将资产文件内容安全保存到 static const 变量中。然而,对于更大的资产,如果它从资产中索引了数千行,这可能会减慢你的 IDE。从分析器中排除这些文件可能会有所帮助: analysis_options.dart

analyzer:
  exclude:
    - '**/assets/data/**'

aspen 包

您还可以签出 aspen 包,它有助于以前的解决方案。您指定资产的路径,然后通过代码生成将这些文件的内容保存到可直接从代码获得的变量中。

part 'assets.g.dart';

// @Asset is an annotation from package:aspen that marks the asset to be packed.
@Asset('asset:my_package/web/my-asset.txt')
// We create a const (it must be const!) value that holds the generated asset content.
const myTextAsset = TextAsset(text: _myTextAsset$content);

考虑到 documentation 你应该使用 rootBundle 加载你的 json:

String   jsonString =  await rootBundle.loadString('assets/b737.json');
  

如果您在 Widget 中使用它,建议用户使用它 DefaultAssetBundle.of(context)

String jsonString = await DefaultAssetBundle
      .of(context)
      .loadString("assets/b737.json");

乍一看这是“不可能的”,因为 Dart 包可以在任何地方使用——例如在非 Flutter 环境中使用。然后就真的找不到了

不过,有一个解决方法:inverse of control。示例代码:

// pure dart package

abstract class AbstractAssetFileFetcherService {
  String getAssetFileContent(String assetName);
}

AbstractAssetFileFetcherService? service;

void myFunctionThatUsesAssetFile() {
  var myFirstFileContent = service.getAssetFileContent('my-first-file-name.png');
  var mySecondFileContent = service.getAssetFileContent('my-second-file-name.json');
  // ... use the file content happily ...
}

并且在你要使用pure-dart包的Flutter包中:

void main() {
  service = AssetFileFetcherService(); // or use other IoC methods such as the `GetIt` Dart package
  myFunctionThatUsesAssetFile();
}

class AssetFileFetcherService extends AbstractAssetFileFetcherService {
  String getAssetFileContent(String assetName) {
    return rootBundle.loadString(assetName); // just use any normal method. you are in Flutter environment here.
  }
}

编辑

Flutter 的资产文件可能不存在于磁盘路径中的任何位置 - 例如可以将其压缩在 apk (android) 文件中。此外,您可能没有足够的权限来执行此操作。

https://flutter.dev/docs/development/ui/assets-and-images#asset-bundling

During a build, Flutter places assets into a special archive called the asset bundle that apps read from at runtime.

试试这个:

final stringData = await File('data.json').readAsString();
final data = json.decode(stringData);