添加资源和图片
Flutter 应用程序包含代码和 assets(也为资源)。资源是被打包到应用程序安装包中,可以在运行时访问的一种文件。常见的资源类型包括静态数据(例如 JSON 文件),配置文件,图标和图片(JPEG,WebP,GIF,动画 WebP / GIF,PNG,BMP 和 WBMP)。
Flutter apps can include both code and assets (sometimes called resources). An asset is a file that is bundled and deployed with your app, and is accessible at runtime. Common types of assets include static data (for example, JSON files), configuration files, icons, and images (JPEG, WebP, GIF, animated WebP/GIF, PNG, BMP, and WBMP).
指定资源
Specifying assets
Flutter 使用 pubspec.yaml
文件,位于项目根目录,来识别应用程序所需的资源。
Flutter uses the pubspec.yaml
file,
located at the root of your project,
to identify assets required by an app.
下面举个例子:
Here is an example:
flutter:
assets:
- assets/my_icon.png
- assets/background.png
如果要包含一个目录下的所有 assets,需要在目录名称的结尾加上 /
:
To include all assets under a directory,
specify the directory name with the /
character at the end:
flutter:
assets:
- directory/
- directory/subdirectory/
资源打包
Asset bundling
该 assets
部分的 flutter
部分需要指定包含在应用程序中的文件。每个资源都通过相对于 pubspec.yaml
文件所在位置的路径进行标识。资源的声明顺序是无关紧要的。资源的实际目录可以是任意文件夹(在第一个样例中是 assets
,其他的是 directory
)
The assets
subsection of the flutter
section
specifies files that should be included with the app.
Each asset is identified by an explicit path
(relative to the pubspec.yaml
file) where the asset
file is located. The order in which the assets are
declared doesn’t matter. The actual directory name used
(assets
in first example or directory
in the above
example) doesn’t matter.
在一次构建中,Flutter 将 assets 放到 asset bundle 的特殊归档中,以便应用程序在运行时读取它们。
During a build, Flutter places assets into a special archive called the asset bundle that apps read from at runtime.
资源变体
Asset variants
构建过程支持 asset 变体:不同版本的资源可能会显示在不同的上下文中。在 pubspec.yaml
的 assets
部分中指定的资源路径,会在构建过程中,查找同级子目录中相同名称的任何文件。这些文件会与指定的资源一起被打包在资源 bundle 中。
The build process supports the notion of asset variants:
different versions of an asset that might be displayed
in different contexts. When an asset’s path is specified
in the assets
section of pubspec.yaml
,
the build process looks for any files with the same
name in adjacent subdirectories. Such files are then
included in the asset bundle along with the specified asset.
例如,你的应用程序目录中有以下文件:
For example, if you have the following files in your application directory:
.../pubspec.yaml
.../graphics/my_icon.png
.../graphics/background.png
.../graphics/dark/background.png
...etc.
…同时 pubspec.yaml
文件包含:
And your pubspec.yaml
file contains the following:
flutter:
assets:
- graphics/background.png
那么这两个图片:
graphics/background.png
和 graphics/dark/background.png
将被打包在你的资源 bundle 中。前者被称为是 main asset,后者被称为是一种变体(variant)。
Then both graphics/background.png
and graphics/dark/background.png
are included in your asset bundle. The former is considered the
main asset, while the latter is considered a variant.
如果指定的是 graphics 目录:
If, on the other hand, the graphics directory is specified:
flutter:
assets:
- graphics/
… 那么 graphics/my_icon.png
,
graphics/background.png
和 graphics/dark/background.png
同时被包含。
Then the graphics/my_icon.png
, graphics/background.png
and graphics/dark/background.png
files are also included.
在选择当前设备分辨率的图片时,Flutter 会使用资源变体;见下文。将来,这种机制可能会扩展到本地化、阅读提示等方面。
Flutter uses asset variants when choosing resolution-appropriate images. In the future, this mechanism might be extended to include variants for different locales or regions, reading directions, and so on.
加载 assets
Loading assets
你的应用程序可以通过 AssetBundle
对象访问其资源。
Your app can access its assets through an
AssetBundle
object.
Asset bundle 通过指定一个逻辑键(key),允许你读取 string/text(loadString
)和 image/binary(load
)。在编译期间,这个逻辑键(key)会映射在 pubspec.yaml
中指定的资源路径。
The two main methods on an asset bundle allow you to load a
string/text asset (loadString()
) or an image/binary asset (load()
)
out of the bundle, given a logical key. The logical key maps to the path
to the asset specified in the pubspec.yaml
file at build time.
加载文本 assets
Loading text assets
每个 Flutter 应用程序都有一个 rootBundle
对象,可以轻松访问主资源 bundle 。还可以直接使用
package:flutter/services.dart
中全局静态的 rootBundle
来加载资源。
Each Flutter app has a rootBundle
object for easy access to the main asset bundle.
It is possible to load assets directly using the
rootBundle
global static from
package:flutter/services.dart
.
但是,如果获取当前 BuildContext
的 AssetBundle
,建议使用 DefaultAssetBundle
。这种方式不是使用应用程序构建的默认资源 bundle,而是让父级 widget 在运行时替换的不同的 AssetBundle,这对于本地化或测试场景很有用。
However, it’s recommended to obtain the AssetBundle
for the current BuildContext
using
DefaultAssetBundle
, rather than the default
asset bundle that was built with the app; this
approach enables a parent widget to substitute a
different AssetBundle
at run time,
which can be useful for localization or testing
scenarios.
通常,你可以从应用程序运行时的 rootBundle
中,间接使用 DefaultAssetBundle.of()
来加载资源(例如 JSON 文件)。
Typically, you’ll use DefaultAssetBundle.of()
to indirectly load an asset, for example a JSON file,
from the app’s runtime rootBundle
.
在 Widget 上下文之外,或 AssetBundle 的句柄不可用时,你可以使用 rootBundle
直接加载这些 assets,例如:
Outside of a Widget
context, or when a handle
to an AssetBundle
is not available,
you can use rootBundle
to directly load such assets.
For example:
import 'package:flutter/services.dart' show rootBundle; Future<String> loadAsset() async { return await rootBundle.loadString('assets/config.json'); }
加载图片
Loading images
Flutter 可以为当前设备加载适合其分辨率的图像。
Flutter can load resolution-appropriate images for the current device pixel ratio.
声明分辨率相关的图片 assets
Declaring resolution-aware image assets
AssetImage
可以将逻辑请求资源映射到最接近当前
设备像素比 的资源。为了使这种映射起作用,应该根据特定的目录结构来保存资源:
AssetImage
understands how to map a logical requested
asset onto one that most closely matches the current
device pixel ratio.
In order for this mapping to work, assets should be arranged
according to a particular directory structure:
.../image.png
.../Mx/image.png
.../Nx/image.png
...etc.
…其中 M 和 N 是数字标识符,对应于其中包含的图像的分辨率,换句话说,它们指定不同设备像素比例的图片。
Where M and N are numeric identifiers that correspond to the nominal resolution of the images contained within. In other words, they specify the device pixel ratio that the images are intended for.
主资源默认对应于 1.0 倍的分辨率图片。比如下面的图片 my_icon.png
:
The main asset is assumed to correspond to a resolution of 1.0.
For example, consider the following asset layout for an
image named my_icon.png
:
.../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
。
On devices with a device pixel ratio of 1.8, the asset
.../2.0x/my_icon.png
is chosen.
For a device pixel ratio of 2.7, the asset
.../3.0x/my_icon.png
is chosen.
如果在 Image
widget 上未指定渲染图像的宽度和高度,通常会扩展资源来保证与主资源相同的屏幕空间量,并不是相同的物理像素,只是分辨率更高。换句话说,.../my_icon.png
是 72 px 乘 72 px,那么 .../3.0x/my_icon.png
应该是 216 px 乘 216 px;但如果未指定宽度和高度,它们都将渲染为 72 px 乘 72 px(以逻辑像素为单位)。
If the width and height of the rendered image are not specified
on the Image
widget, the nominal resolution is used to scale
the asset so that it occupies the same amount of screen space
as the main asset would have, just with a higher resolution.
That is, if .../my_icon.png
is 72px by 72px, then
.../3.0x/my_icon.png
should be 216px by 216px;
but they both render into 72px by 72px (in logical pixels),
if width and height are not specified.
在 pubspec.yaml
中资源部分的每一项都应与实际文件相对应,除过主资源节点。当主资源缺少某个文件时,会按分辨率从低到高的顺序去选择,也就是说 1x 中没有的话会在 2x 中找,2x 中还没有的话就在 3x 中找。该条目需要在 pubspec.yaml
中指定。
Each entry in the asset section of the pubspec.yaml
should correspond to a real file, with the exception of
the main asset entry. If the main asset entry doesn’t correspond
to a real file, then the asset with the lowest resolution
is used as the fallback for devices with device pixel
ratios below that resolution. The entry should still
be included in the pubspec.yaml
manifest, however.
加载 images
Loading images
加载图片,请在 widget 的 build
方法中使用 AssetImage
类。
To load an image, use the AssetImage
class in a widget’s build()
method.
例如,你的应用程序可以从上面的资源声明中加载背景图片:
For example, your app can load the background image from the asset declarations above:
return const Image(image: AssetImage('graphics/background.png'));
使用默认的资源 bundle 加载资源时,系统会自动处理分辨率等。(如果你使用一些更低级别的类,如 ImageStream
或
ImageCache
,你需要注意 scale 相关的参数)。
Anything using the default asset bundle inherits resolution
awareness when loading images. (If you work with some of the lower
level classes, like ImageStream
or ImageCache
,
you’ll also notice parameters related to scale.)
依赖包中的资源图片
Asset images in package dependencies
加载依赖 package 中的图像,必须给 AssetImage
提供 package
参数。
To load an image from a package dependency,
the package
argument must be provided to AssetImage
.
例如,你的应用程序依赖于一个名为 my_icons
的 package,它的目录结构如下:
For instance, suppose your application depends on a package
called my_icons
, which has the following directory structure:
.../pubspec.yaml
.../icons/heart.png
.../icons/1.5x/heart.png
.../icons/2.0x/heart.png
...etc.
然后加载 image, 使用:
To load the image, use:
return const AssetImage('icons/heart.png', package: 'my_icons');
package 使用本身的 Assets 也需要加上 package
参数来获取。
Assets used by the package itself should also be fetched
using the package
argument as above.
打包 assets
Bundling of package assets
如果期望的资源文件被指定在 package 的 pubspec.yaml
文件中,它会被自动打包到应用程序中。特别是,package 本身使用的资源必须在 pubspec.yaml
中指定。
If the desired asset is specified in the pubspec.yaml
file of the package, it’s bundled automatically with the
application. In particular, assets used by the package
itself must be specified in its pubspec.yaml
.
package 也可以选择在其 lib/
文件夹中包含未在 pubspec.yaml
文件中声明的资源。在这种情况下,对于要打包的图片,应用程序必须在 pubspec.yaml
中指定包含哪些图像。例如,一个名为 fancy_backgrounds
的包,可能包含以下文件:
A package can also choose to have assets in its lib/
folder that are not specified in its pubspec.yaml
file.
In this case, for those images to be bundled,
the application has to specify which ones to include in its
pubspec.yaml
. For instance, a package named fancy_backgrounds
could have the following files:
.../lib/backgrounds/background1.png
.../lib/backgrounds/background2.png
.../lib/backgrounds/background3.png
总而言之,要包含第一张图像,必须在 pubspec.yaml
的 assets
部分中声明它:
To include, say, the first image, the pubspec.yaml
of the
application should specify it in the assets
section:
flutter:
assets:
- packages/fancy_backgrounds/backgrounds/background1.png
lib/
是隐含的,所以它不应该包含在资源路径中。
The lib/
is implied,
so it should not be included in the asset path.
如果你正在开发 package,想要从 package 中加载资源,首先要在 pubspec.yaml 中定义:
If you are developing a package, to load an asset within the package, specify it in the ‘pubspec.yaml’ of the package:
flutter:
assets:
- assets/images/
在 package 中加载图片,按以下方式:
To load the image within your package, use:
return const AssetImage('packages/fancy_backgrounds/backgrounds/background1.png');
平台共享 assets
Sharing assets with the underlying platform
在不同平台读取 Flutter assets,
Android 是通过 AssetManager
,iOS 是 NSBundle
。
Flutter assets are readily available to platform code
using the AssetManager
on Android and NSBundle
on iOS.
在 Android 中加载 Flutter 资源文件
Loading Flutter assets in Android
在 Android 平台上,assets 通过 AssetManager
API 读取。通过 PluginRegistry.Registrar
的 lookupKeyForAsset
方法,或者 FlutterView
的 getLookupKeyForAsset
方法来获取文件路径,然后 AssetManager
的 openFd
根据文件路径得到文件描述符。开发插件时可以使用 PluginRegistry.Registrar
,而开发应用程序使用平台视图时,FlutterView
是最好的选择。
On Android the assets are available through 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.
举个例子,假设你在 pubspec.yaml 中这样指定:
As an example, suppose you have specified the following in your pubspec.yaml
flutter:
assets:
- icons/heart.png
在你的 Flutter 应用程序对应以下结构。
This reflects the following structure in your Flutter app.
.../pubspec.yaml
.../icons/heart.png
...etc.
想要在 Java 插件中访问 icons/heart.png
;
To access icons/heart.png
from your Java plugin code,
do the following:
AssetManager assetManager = registrar.context().getAssets();
String key = registrar.lookupKeyForAsset("icons/heart.png");
AssetFileDescriptor fd = assetManager.openFd(key);
在 iOS 中加载 Flutter 资源文件
Loading Flutter assets in iOS
在 iOS 平台上,assets 资源文件通过 mainBundle
读取。通过 pathForResource:ofType:
的 lookupKeyForAsset
或者 lookupKeyForAsset:fromPackage:
方法获取文件路径,同样,FlutterViewController
的 lookupKeyForAsset:
或者 lookupKeyForAsset:fromPackage:
方法也可以获取文件路径。开发插件时可以使用 FlutterPluginRegistrar
,而开发应用程序使用平台视图时, FlutterViewController
是最好的选择。
On iOS the assets are available through 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.
举个例子,假设你的 Flutter 配置和上面一样。
As an example, suppose you have the Flutter setting from above.
要在 Objective-C 插件中访问 icons/heart.png
:
To access icons/heart.png
from your Objective-C plugin code you
would do the following:
NSString* key = [registrar lookupKeyForAsset:@"icons/heart.png"];
NSString* path = [[NSBundle mainBundle] pathForResource:key ofType:nil];
这有一个更完整的实例可以理解 Flutter 的应用:video_player
plugin。
For a more complete example, see the implementation of the
Flutter video_player
plugin on pub.dev.
pub.dev 上的 ios_platform_images
plugin 将这些逻辑封装成方便的类别。它允许编写:
The ios_platform_images
plugin on pub.dev wraps
up this logic in a convenient category. You fetch
an image as follows:
Objective-C:
[UIImage flutterImageWithName:@"icons/heart.png"];
Swift:
UIImage.flutterImageNamed("icons/heart.png")
在 Flutter 中加载 iOS 的图片
Loading iOS images in Flutter
When implementing Flutter by
adding it to an existing iOS app,
you might have images hosted in iOS that you
want to use in Flutter. To accomplish
that, use the ios_platform_images
plugin
available on pub.dev.
平台 assets
Platform assets
某些场景可以直接在平台项目中使用 assets。以下是在 Flutter 框架加载并运行之前使用资源的两种常见情况。
There are other occasions to work with assets in the platform projects directly. Below are two common cases where assets are used before the Flutter framework is loaded and running.
更新桌面图标
Updating the app icon
更新你的 Flutter 应用程序启动图标,和原生 Android 或 iOS 应用程序中更新启动图标的方法相同。
Updating a Flutter application’s launch icon works the same way as updating launch icons in native Android or iOS applications.
Android
在 Flutter 项目的根目录中,导航到 .../android/app/src/main/res
路径。各种位图资源文件夹,比如 mipmap-hdpi
,已包含占位符图像 ic_launcher.png
。只需按照 Android 开发者指南 中的说明,将其替换为所需的资源,并遵守每种屏幕分辨率的建议图标大小标准。
In your Flutter project’s root directory, navigate to
.../android/app/src/main/res
. The various bitmap resource
folders such as mipmap-hdpi
already contain placeholder
images named ic_launcher.png
. Replace them with your
desired assets respecting the recommended icon size per
screen density as indicated by the Android Developer Guide.
iOS
在你的 Flutter 项目的根目录中,导航到 .../ios/Runner
路径。该目录中 Assets.xcassets/AppIcon.appiconset
已经包含占位符图片,只需将它们替换为适当大小的图片,并且根据 iOS 开发指南,文件名称保持不变。
In your Flutter project’s root directory,
navigate to .../ios/Runner
. The
Assets.xcassets/AppIcon.appiconset
directory already contains
placeholder images. Replace them with the appropriately
sized images as indicated by their filename as dictated by the
Apple Human Interface Guidelines.
Keep the original file names.
更新启动图
Updating the launch screen
在 Flutter 框架加载时,Flutter 会使用原生平台机制绘制启动页。此启动页将持续到 Flutter 渲染应用程序的第一帧。
Flutter also uses native platform mechanisms to draw transitional launch screens to your Flutter app while the Flutter framework loads. This launch screen persists until Flutter renders the first frame of your application.
Android
将启动屏幕「splash screen」添加到你的 Flutter 应用程序,请导航至 .../android/app/src/main
路径。在 res/drawable/launch_background.xml
文件中,通过使用
图层列表 XML 来实现自定义启动页。现有模板提供了一个示例,用于将图片添加到白色启动页的中间(注释代码中)。你也可以取消注释使用 可绘制对象资源 来实现预期效果。
To add a “splash screen” to your Flutter application,
navigate to .../android/app/src/main
.
In res/drawable/launch_background.xml
,
use this layer list drawable XML to customize
the look of your launch screen. The existing template provides
an example of adding an image to the middle of a white splash
screen in commented code. You can uncomment it or use other
drawables to achieve the intended effect.
更多详细信息,请查看 在 Android 应用中添加闪屏页与启动页。
For more details, see Adding a splash screen to your mobile app.
iOS
将图片添加到启动屏幕「splash screen」的中心,请导航至 .../ios/Runner
路径。在 Assets.xcassets/LaunchImage.imageset
,拖入图片,并命名为 LaunchImage.png
, LaunchImage@2x.png
,LaunchImage@3x.png
。如果你使用不同的文件名,那你还必须更新同一目录中的 Contents.json
文件中对应的名称。
To add an image to the center of your “splash screen”,
navigate to .../ios/Runner
.
In Assets.xcassets/LaunchImage.imageset
,
drop in images named LaunchImage.png
,
LaunchImage@2x.png
, LaunchImage@3x.png
.
If you use different filenames,
update the Contents.json
file in the same directory.
你也可以通过打开 .../ios/Runner.xcworkspace
,完全自定义 storyboard。在 Project Navigator 中导航到 Runner/Runner
,然后打开 Assets.xcassets
拖入图片,或者在 LaunchScreen.storyboard
中使用 Interface Builder 进行自定义。
You can also fully customize your launch screen storyboard
in Xcode by opening .../ios/Runner.xcworkspace
.
Navigate to Runner/Runner
in the Project Navigator and
drop in images by opening Assets.xcassets
or do any
customization using the Interface Builder in
LaunchScreen.storyboard
.
更多详细信息,请查看 在 Android 应用中添加闪屏页与启动页。
For more details, see Adding a splash screen to your mobile app.