添加资源和图片

Flutter 应用程序包含代码和 assets(也为资源)。资源是被打包到应用程序安装包中,可以在运行时访问的一种文件。常见的资源类型包括静态数据(例如 JSON 文件),配置文件,图标和图片(JPEG,WebP,GIF,动画 WebP / GIF,PNG,BMP 和 WBMP)。

指定资源

#

Flutter 使用 pubspec.yaml 文件,位于项目根目录,来识别应用程序所需的资源。

下面举个例子:

yaml
flutter:
  assets:
    - assets/my_icon.png
    - assets/background.png

如果要包含一个目录下的所有 assets,需要在目录名称的结尾加上 /

yaml
flutter:
  assets:
    - directory/
    - directory/subdirectory/

Asset bundling (应用打包资源)

#

yaml 文件 flutter 下面的 assets 部分指定了需要包含在应用中的文件。每个资源都通过相对于 pubspec.yaml 文件所在位置的路径进行标识。资源的声明顺序是无关紧要的。资源的实际目录可以是任意文件夹(在第一个样例中是 assets,其他的是 directory

在一次构建中,Flutter 将 assets 放到 asset bundle 的特殊归档中,以便应用在运行时读取它们。

Automatic transformation of asset files at build time

#

Flutter supports using a Dart package to transform asset files when building your app. To do this, specify the asset files and transformer package in your pubspec file. To learn how to do this and write your own asset-transforming packages, see Transforming assets at build time.

Conditional bundling of assets based on app flavor

#

If your project utilizes the flavors feature, you can configure individual assets to be bundled only in certain flavors of your app. For more information, check out Conditionally bundling assets based on flavor.

加载 assets

#

你的应用程序可以通过 AssetBundle 对象访问其资源。

Asset bundle 通过指定一个逻辑键(key),允许你读取 string/text(loadString)和 image/binary(load)。在编译期间,这个逻辑键(key)会映射在 pubspec.yaml 中指定的资源路径。

加载文本 assets

#

每个 Flutter 应用程序都有一个 rootBundle 对象,可以轻松访问主资源 bundle 。还可以直接使用 package:flutter/services.dart 中全局静态的 rootBundle 来加载资源。

但是,如果获取当前 BuildContextAssetBundle,建议使用 DefaultAssetBundle。这种方式不是使用应用程序构建的默认资源 bundle,而是让父级 widget 在运行时替换的不同的 AssetBundle,这对于本地化或测试场景很有用。

通常,你可以从应用程序运行时的 rootBundle 中,间接使用 DefaultAssetBundle.of() 来加载资源(例如 JSON 文件)。

在 Widget 上下文之外,或 AssetBundle 的句柄不可用时,你可以使用 rootBundle 直接加载这些 assets,例如:

dart
import 'package:flutter/services.dart' show rootBundle;

Future<String> loadAsset() async {
  return await rootBundle.loadString('assets/config.json');
}

加载图片

#

你可以在 build() 方法中使用 AssetImage 加载图片。

举个例子,下面的代码加载了先前声明的背景图片:

dart
return const Image(image: AssetImage('assets/background.png'));

分辨率自适应图片资源

#

Flutter 可以为当前设备加载适合其 设备像素比 的图像。

AssetImage 可以将请求资源映射到最接近当前 设备像素比 的资源。

为了使这种映射起作用,资源应该根据特定的目录结构来保存:

.../image.png
.../Mx/image.png
.../Nx/image.png
...etc.

其中 MN 是数字标识符,对应于其中包含的图像的分辨率,换句话说,它们指定不同设备像素比例的图片。

在示例中,image.png主资源,而 Mx/image.pngNx/image.png 则被认为是 变体

主资源默认对应于 1.0 倍的分辨率图片。比如下面的图片 my_icon.png

.../my_icon.png       (mdpi baseline)
.../1.5x/my_icon.png  (hdpi)
.../2.0x/my_icon.png  (xhdpi)
.../3.0x/my_icon.png  (xxhdpi)
.../4.0x/my_icon.png  (xxxhdpi)

而在设备像素比率为 1.8 的设备上,对应是 .../2.0x/my_icon.png 。如果是 2.7 的设备像素比,对应是 .../3.0x/my_icon.png

如果在 Image widget 上未指定渲染图像的宽度和高度,通常会扩展资源来保证与主资源相同的屏幕空间量,并不是相同的物理像素,只是分辨率更高。换句话说,.../my_icon.png 是 72 px 乘 72 px,那么 .../3.0x/my_icon.png 应该是 216 px 乘 216 px;但如果未指定宽度和高度,它们都将渲染为 72 px 乘 72 px(以逻辑像素为单位)。

Bundling of resolution-aware image assets

#

你只需要在 pubspec.yamlassets 部分指定主要资源, Flutter 会自动帮你绑定其他变体。在 pubspec.yaml 中资源部分的每一项都应与实际文件相对应,除过主资源节点。当主资源缺少某个文件时,会按分辨率从低到高的顺序去选择,也就是说 1x 中没有的话会在 2x 中找,2x 中还没有的话就在 3x 中找。该条目需要在 pubspec.yaml 中指定。

使用默认的资源 bundle 加载资源时,系统会自动处理分辨率等。(如果你使用一些更低级别的类,如 ImageStreamImageCache,你需要注意 scale 相关的参数)。

依赖包中的资源图片

#

加载依赖 package 中的图像,必须给 AssetImage 提供 package 参数。

例如,你的应用程序依赖于一个名为 my_icons 的 package,它的目录结构如下:

.../pubspec.yaml
.../icons/heart.png
.../icons/1.5x/heart.png
.../icons/2.0x/heart.png
...etc.

然后加载 image, 使用:

dart
return const AssetImage('icons/heart.png', package: 'my_icons');

package 使用本身的 Assets 也需要加上 package 参数来获取。

打包 assets

#

如果期望的资源文件被指定在 package 的 pubspec.yaml 文件中,它会被自动打包到应用程序中。特别是,package 本身使用的资源必须在 pubspec.yaml 中指定。

package 也可以选择在其 lib/ 文件夹中包含未在 pubspec.yaml 文件中声明的资源。在这种情况下,对于要打包的图片,应用程序必须在 pubspec.yaml 中指定包含哪些图像。例如,一个名为 fancy_backgrounds 的包,可能包含以下文件:

.../lib/backgrounds/background1.png
.../lib/backgrounds/background2.png
.../lib/backgrounds/background3.png

总而言之,要包含第一张图像,必须在 pubspec.yamlassets 部分中声明它:

yaml
flutter:
  assets:
    - packages/fancy_backgrounds/backgrounds/background1.png

lib/ 是隐含的,所以它不应该包含在资源路径中。

如果你正在开发 package,想要从 package 中加载资源,首先要在 pubspec.yaml 中定义:

yaml
flutter:
  assets:
    - assets/images/

在 package 中加载图片,按以下方式:

dart
return const AssetImage('packages/fancy_backgrounds/backgrounds/background1.png');

平台共享 assets

#

在不同平台读取 Flutter assets, Android 是通过 AssetManager,iOS 是 NSBundle

在 Android 中加载 Flutter 资源文件

#

在 Android 平台上,assets 通过 AssetManager API 读取。通过 PluginRegistry.RegistrarlookupKeyForAsset 方法,或者 FlutterViewgetLookupKeyForAsset 方法来获取文件路径,然后 AssetManageropenFd 根据文件路径得到文件描述符。开发插件时可以使用 PluginRegistry.Registrar,而开发应用程序使用平台视图时,FlutterView 是最好的选择。

举个例子,假设你在 pubspec.yaml 中这样指定:

yaml
flutter:
  assets:
    - icons/heart.png

在你的 Flutter 应用程序对应以下结构。

.../pubspec.yaml
.../icons/heart.png
...etc.

想要在 Java 插件中访问 icons/heart.png

java
AssetManager assetManager = registrar.context().getAssets();
String key = registrar.lookupKeyForAsset("icons/heart.png");
AssetFileDescriptor fd = assetManager.openFd(key);

在 iOS 中加载 Flutter 资源文件

#

在 iOS 平台上,assets 资源文件通过 mainBundle 读取。通过 pathForResource:ofType:lookupKeyForAsset 或者 lookupKeyForAsset:fromPackage: 方法获取文件路径,同样,FlutterViewControllerlookupKeyForAsset: 或者 lookupKeyForAsset:fromPackage: 方法也可以获取文件路径。开发插件时可以使用 FlutterPluginRegistrar,而开发应用程序使用平台视图时, FlutterViewController 是最好的选择。

举个例子,假设你的 Flutter 配置和上面一样。

要在 Objective-C 插件中访问 icons/heart.png

objc
NSString* key = [registrar lookupKeyForAsset:@"icons/heart.png"];
NSString* path = [[NSBundle mainBundle] pathForResource:key ofType:nil];

要在 Swift 应用程序中访问 icons/heart.png

swift
let key = controller.lookupKey(forAsset: "icons/heart.png")
let mainBundle = Bundle.main
let path = mainBundle.path(forResource: key, ofType: nil)

这有一个更完整的实例可以理解 Flutter 的应用: video_player plugin

pub.dev 上的 ios_platform_images plugin 将这些逻辑封装成方便的类别。它允许编写:

Objective-C:

objc
[UIImage flutterImageWithName:@"icons/heart.png"];

Swift:

swift
UIImage.flutterImageNamed("icons/heart.png")

在 Flutter 中加载 iOS 的图片

#

当你在 iOS 应用程序中添加 Flutter 时,你可能希望在 Flutter 中使用 iOS 中的图片。为了实现这一点,可以使用 pub.dev 上的 ios_platform_images 插件。

平台 assets

#

某些场景可以直接在平台项目中使用 assets。以下是在 Flutter 框架加载并运行之前使用资源的两种常见情况。

更新桌面图标

#

更新你的 Flutter 应用程序启动图标,和原生 Android 或 iOS 应用程序中更新启动图标的方法相同。

Launch icon

Android

#

在 Flutter 项目的根目录中,导航到 .../android/app/src/main/res 路径。各种位图资源文件夹,比如 mipmap-hdpi,已包含占位符图像 ic_launcher.png。只需按照 Android 开发者指南 中的说明,将其替换为所需的资源,并遵守每种屏幕分辨率的建议图标大小标准。

Android icon location

iOS

#

在你的 Flutter 项目的根目录中,导航到 .../ios/Runner 路径。该目录中 Assets.xcassets/AppIcon.appiconset已经包含占位符图片,只需将它们替换为适当大小的图片,并且根据 iOS 开发指南,文件名称保持不变。

iOS icon location

更新启动图

#

Launch screen

在 Flutter 框架加载时,Flutter 会使用原生平台机制绘制启动页。此启动页将持续到 Flutter 渲染应用程序的第一帧。

Android

#

将启动屏幕「splash screen」添加到你的 Flutter 应用程序,请导航至 .../android/app/src/main 路径。在 res/drawable/launch_background.xml 文件中,通过使用 图层列表 XML 来实现自定义启动页。现有模板提供了一个示例,用于将图片添加到白色启动页的中间(注释代码中)。你也可以取消注释使用 可绘制对象资源 来实现预期效果。

更多详细信息,请查看 在 Android 应用中添加闪屏页与启动页

iOS

#

将图片添加到启动屏幕「splash screen」的中心,请导航至 .../ios/Runner 路径。在 Assets.xcassets/LaunchImage.imageset ,拖入图片,并命名为 LaunchImage.pngLaunchImage@2x.pngLaunchImage@3x.png。如果你使用不同的文件名,那你还必须更新同一目录中的 Contents.json 文件中对应的名称。

你也可以通过打开 .../ios/Runner.xcworkspace ,完全自定义 storyboard。在 Project Navigator 中导航到 Runner/Runner ,然后打开 Assets.xcassets 拖入图片,或者在 LaunchScreen.storyboard 中使用 Interface Builder 进行自定义。

Adding launch icons in Xcode

更多详细信息,请查看 在 iOS 应用中添加闪屏页与启动页