在开发Flutter APK
时,我们可以将一些资源预置在APP
中,这些资源可以分为以下几类:
- 文本
- 图片
为了统一管理,我们将它们统一放在assets/
目录下,然后介绍一下如何对它们进行读取。
一、文本
文本一般用于存储默认数据,在第一次进入无网的情况下进行数据的展示。
在pubspec.yaml
的flutter
标签下,声明要加载的文件名:
flutter:
assets :
- assets/files/hello.txt
文本的加载有两种方式:
- 使用全局的静态
rootBundle
对象,需要导入package:flutter/services.dart
,好处是不需要提供BuildContext
。 - 使用
DefaultAssetBundle
获取当前BuildContext
的AssetBundle
来加载。
两种方式的示例代码如下,分别对应于_loadAssetFile()
和_loadAssetFile2()
:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(AssetDemo());
class AssetDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Assets Demo')),
body: AssetFilesWidget(),
),
);
}
}
class AssetFilesWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _AssetFilesWidgetState();
}
}
class _AssetFilesWidgetState extends State<AssetFilesWidget> {
String text;
@override
void initState() {
super.initState();
_loadAssetFile2();
}
@override
Widget build(BuildContext context) {
return Text(text ?? 'default');
}
_loadAssetFile() async {
String text = await rootBundle.loadString('assets/files/hello.txt');
print(text);
setState(() {
this.text = text;
});
}
_loadAssetFile2() async {
String text = await DefaultAssetBundle.of(context).loadString('assets/files/hello.txt');
setState(() {
this.text = text;
});
}
}
二、图片
图片的加载和文本类似,也是需要先进行声明再使用。
在Android
中,我们会将图片放在res/
下对应的drawable-?
文件夹中,而放在不同的文件夹中,根据设备dpi
(每英寸图片上点的个数)的不同,最终加载到内存中的图片大小是不一样的,在Flutter
中也有类似的概念。
关于Android
中相关的概念可以查看这篇文章 图片基础知识梳理(2) - Bitmap 占用内存分析。
在Flutter
中,也有和drawable-?
相同的设计,用N.X
来代替:
其加载的逻辑为:
- 根目录默认对应于
1.0
分辨率的图片。 - 设备在选择图片时,将会选择离它
dpiDensity
最近的文件夹。假如dpiDensity=1.8
,那么会选择2.0x
目录下的资源。(dpiDensity
用MediaQuery.of(context).devicePixelRatio
来获取。) - 确定了需要选择哪个文件夹的图片后,假如没有设置
Image Widget
控件的大小,那么最终加载显示的宽高还要用原始图片宽高再除以对应文件夹的系数。
示例如下:
在pubspec.yaml
中声明:
flutter:
uses-material-design: true
assets :
- assets/files/hello.txt
- assets/images/
使用Image(key : imageKey, image : AssetImage('assets/images/pic.png'))
加载图片。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(AssetDemo());
class AssetDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Assets Demo')),
body: AssetImageWidget(),
),
);
}
}
class AssetImageWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _AssetImageWidgetState();
}
}
class _AssetImageWidgetState extends State<AssetImageWidget> {
String text;
GlobalKey imageKey = new GlobalKey();
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
RaisedButton(onPressed: () {
text = 'height=${imageKey.currentContext.size.height}'
',width=${imageKey.currentContext.size.height}'
',dpi=${MediaQuery.of(context).devicePixelRatio}';
print(text);
}, child: Text('refresh')),
Image(key : imageKey, image : AssetImage('assets/images/pic.png'))
]);
}
}
为了验证之前的加载逻辑,试验了以下三种情况:
- 在主资源
assets/images
目录下添加pic.png
,结果为:
height=96.0,width=96.0,dpi=3.0
- 在主资源和
assets/images/3.0x
目录下添加pic.png
,结果为:
height=32.0,width=32.0,dpi=3.0
- 在主资源和
assets/images/2.0x
目录下添加pic.png
,结果为:
height=48.0,width=48.0,dpi=3.0
这里有点需要注意,当我们改变资源目录的结构时,需要修改pubspec.yaml
才能生效。
三、使用第三方包中的图片
3.1 应用程序使用第三方包中的图片
当应用程序依赖于包名为my_icons
的包,加载图像的方式为:
AssetImage('icons/heart.png', package: 'my_icons')
3.2 打包第三方图片
对于第三方包中的图片,有以下几种情况:
- 第三方包自己使用的资源,那么必须要在它自身的
pubspec.yaml
中声明,并且使用的时候要带上自己的包名。 - 第三方包提供给其它人使用,但自身不使用,分为两种情况:
- 无论使用者是否用到都强制打包。这种情况下,第三方包需要在它的
pubspec.yaml
中声明,使用者则不需要。 - 由使用者选择是否打包。第三方包将资源放在
lib/
目录下并且不在pubspec.yaml
中声明,由使用者根据使用情况在它自己的pubspec.yaml
中声明。
- 无论使用者是否用到都强制打包。这种情况下,第三方包需要在它的
例如第三方包名为fancy_backgrounds
,它的资源有:
…/lib/backgrounds/background1.png
…/lib/backgrounds/background2.png
…/lib/backgrounds/background3.png
假如使用者希望使用background1
,那么就要在它自己的pubspec.yaml
中声明,注意这里省略了隐式的lib
目录:
flutter:
assets:
- packages/fancy_backgrounds/backgrounds/background1.png