Flutter app 包含code和assets(资源). asset文件夹和app一起打包部署, 在运行过程中可以被访问. 通常类型的asset包含静态数据(如JSON文件), 配置文件, 图标, 图片(JPEG, WebP, GIF, animated WebP/GIF, PNG, BMP, and WBMP)
使用位于根路径的pubspec.yaml
文件, 来识别app所需的asset.
例如:
flutter:
assets:
- assets/my_icon.png
- assets/background.png
如果想要包含assets中文件夹下的所有资源, 指定文件夹名字 + /
flutter:
assets:
- assets/
注意, 只有直接包含在当前文件夹下的文件才会被包含. 如果想添加子文件夹下面的文件, 给每个文件夹创建一个入口
assets中指定了需要包含到app的文件. 每个asset有一个明确的文件路径(相对pubspec.yaml的路径).
编译阶段, Flutter将assets归档成一个特殊的文件, 叫asset bundle. app在运行时, 可以读取它的内容.
编译操作支持asset variants概念: 不同版本的asset可能会显示不同内容. 当pubspec.yaml的assets区中指定了某个asset的路径, 编译过程中会查找有同样名字的文件或者文件夹. 这些文件与指定的asset一起, 被包含到asset bundle中.
例如, 有以下文件在app的文件夹中
.../pubspec.yaml
.../graphics/my_icon.png
.../graphics/background.png
.../graphics/dark/background.png
...etc.
在pubspec.yaml中包含:
flutter:
assets:
- graphics/background.png
这样的话, graphics/background.png
和 graphics/dark/background.png
会被包含到asset bundle中. 前一个叫main asset, 后一个叫 variant
另外, 如果指定的是graphics文件夹:
flutter:
assets:
- graphics/
graphics/my_icon.png
, graphics/background.png
和 graphics/dark/background.png
会被包含.
Flutter使用asset variants来选择合适的图片资源. 未来该机制可能会拓展.
使用AssetBundle
对象来访问app中的assets
根据给定的逻辑key, asset bundle有两个方法让你可以加载字符串/文本资源(loadString)或 图片/二进制资源(load). 在编译过程中, key和pubspec.yaml指定的资源路径会进行映射.
每个Flutter app有一个rootBundle对象, 来更好地访问main asset bundle. 使用rootBundle(package:flutter/services.dart
)直接加载资源
然而, 推荐使用DefaultAssetBundle来获取当前BuildContext的AssetBundle. 而不是使用app默认的asset bundle, 这个方法允许父widget在运行过程中替换不同的AssetBundle, 对本地化或者测试环境非常有用.
通常情况下, 使用DefaultAssetBundle.of()来间接地从rootBundle中加载某个asset, 如JSON文件.
Widget内容之外, 或者无法使用AssetBundle的情况, 可以使用rootBundle直接接在assets, 如
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
Future<String> loadAsset() async {
return await rootBundle.loadString('assets/config.json');
}
Flutter可以根据当前设备的像素比加载合适分辨率的图片
AssetImage知道, 如何匹配与当前设备像素比最为接近的资源. 为了匹配可以成功, assets需要按照特定的文件结构进行安排.
.../image.png
.../Mx/image.png
.../Nx/image.png
...etc.
M and N 是数字标识, 符合图片包含的像素分辨率. 换句话说, 他们指明了图片用在哪个像素比的设备上.
假设main asset符合1.0像素. 如,
.../my_icon.png
.../2.0x/my_icon.png
.../3.0x/my_icon.png
在1.8像素比的设备中, 会选择.../2.0x/my_icon.png
的资源. 在2.7像素比的设备中, 会选择.../3.0x/my_icon.png
资源.
如果Image widget中所渲染的图片没有指定宽高, 像素分辨率会按比例显示资源, 它有更高的像素, 但会和main asset占用同样的屏幕空间. 也就是, 如果.../my_icon.png
是72px x 72px, 则.../3.0x/my_icon.png
是 216px by 216px; 但是如果它们的宽高没有指定, 都会渲染为72px x 72px(逻辑像素)
pubspec.yaml中asset区中的每个入口都需要符合一个真实的文件, main asset的入口除外. 如果main asset入口没有符合一个真实文件, 会使用最低像素的asset在低于该像素比的设备上使用. 然而, 它的入口仍需要在pubspec.yaml中体现.
在widget的build方法中, 使用AssetImage来加载图片.
如, 加载背景图
Widget build(BuildContext context) {
// ...
return DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('graphics/background.png'),
// ...
),
// ...
),
);
// ...
}
使用默认asset bundle加载图片时会继承分辨率识别.(如果使用更低等级的类, 如 ImageStream 或 ImageCache, 你会发现参数与比例相关联)
加载 package依赖中的图片. 需提供一个package参数给AssetImage.
如, 假设你的app依赖一个my_icons package, 文件夹结构如下.
.../pubspec.yaml
.../icons/heart.png
.../icons/1.5x/heart.png
.../icons/2.0x/heart.png
...etc.
为了加载图片, 使用
AssetImage('icons/heart.png', package: 'my_icons')
package自身使用到资源时, 也需要传入如上参数.
目标asset, 如果在pubspec.yaml文件中指定了packaget, 该资源会自动打包. 特别是package中自身使用到的assets必须要在pubspec.yaml中指明.
一个package也可以选择将, assets保存在lib/文件夹中, 而不是指定在 pubspec.yaml文件中. 这种情况下, app需在pubspec.yaml中指明哪几个需要被包含, 来打包那些图片.
例如, 名为fancy_backgrounds的package有以下文件
.../lib/backgrounds/background1.png
.../lib/backgrounds/background2.png
.../lib/backgrounds/background3.png
为了包含, 假设是第一个图片, 需要在app的pubspec.yaml中的assets区域指明
flutter:
assets:
- packages/fancy_backgrounds/backgrounds/background1.png
lib/
是隐式的, 因此不需要包含在asset路径中
Flutter assets可以使用安卓的AssetManager和iOS的NSBundle访问
On Android the assets are available via the AssetManager API. The lookup key used in for instance openFd is obtained from lookupKeyForAsset on PluginRegistry.Registrar or getLookupKeyForAsset on FlutterView. PluginRegistry.Registrar is available when developing a plugin while FlutterView would be the choice when developing an app including a platform view.
As an example, suppose you have specified this in your pubspec.yaml
flutter:
assets:
- icons/heart.png
reflecting the following structure in your Flutter app.
.../pubspec.yaml
.../icons/heart.png
...etc.
To access icons/heart.png from your Java plugin code you would do;
AssetManager assetManager = registrar.context().getAssets();
String key = registrar.lookupKeyForAsset("icons/heart.png");
AssetFileDescriptor fd = assetManager.openFd(key);
On iOS the assets are available via the mainBundle. The lookup key used in for instance pathForResource:ofType: is obtained from lookupKeyForAsset or lookupKeyForAsset:fromPackage: on FlutterPluginRegistrar or lookupKeyForAsset: or lookupKeyForAsset:fromPackage: on FlutterViewController. FlutterPluginRegistrar is available when developing a plugin while FlutterViewController would be the choice when developing an app including a platform view.
As an example, suppose you have the Flutter setting from above.
To access icons/heart.png from your Objective-C plugin code you would do;
NSString* key = [registrar lookupKeyForAsset:@"icons/heart.png"];
NSString* path = [[NSBundle mainBundle] pathForResource:key ofType:nil];
For a more complete example see the implementation of the Flutter video_player plugin.
App图标和启动页 Doc