在开发Flutter APK时,我们可以将一些资源预置在APP中,这些资源可以分为以下几类:

  • 文本
  • 图片

为了统一管理,我们将它们统一放在assets/目录下,然后介绍一下如何对它们进行读取。

frc 0789cbbafd896a179f2fa46b14c69adc - Flutter 知识梳理 (资源加载) - Flutter APK 文件、图片、字体的加载
image.png

一、文本

文本一般用于存储默认数据,在第一次进入无网的情况下进行数据的展示。

pubspec.yamlflutter标签下,声明要加载的文件名:

flutter:

  assets :
    - assets/files/hello.txt

文本的加载有两种方式:

  • 使用全局的静态rootBundle对象,需要导入package:flutter/services.dart,好处是不需要提供BuildContext
  • 使用DefaultAssetBundle获取当前BuildContextAssetBundle来加载。

两种方式的示例代码如下,分别对应于_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 createState() {
    return _AssetFilesWidgetState();
  }

}

class _AssetFilesWidgetState extends State {

  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 占用内存分析。

frc 070e9911a016693efe00a563016ba0d5 - Flutter 知识梳理 (资源加载) - Flutter APK 文件、图片、字体的加载
Android 下的目录

Flutter中,也有和drawable-?相同的设计,用N.X来代替:

frc 307c5842a9c16c86cf4f1e8ed40f39b3 - Flutter 知识梳理 (资源加载) - Flutter APK 文件、图片、字体的加载

其加载的逻辑为:

  • 根目录默认对应于1.0分辨率的图片。
  • 设备在选择图片时,将会选择离它dpiDensity最近的文件夹。假如dpiDensity=1.8,那么会选择2.0x目录下的资源。(dpiDensityMediaQuery.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 createState() {
    return _AssetImageWidgetState();
  }
  
}

class _AssetImageWidgetState extends State {
  
  String text;
  GlobalKey imageKey = new GlobalKey();
  
  @override
  Widget build(BuildContext context) {
    return Column(children: [
      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

参考文章

  • 在 Flutter 中添加资源和图片
  • 图片基础知识梳理(2) – Bitmap 占用内存分析

Flutter 知识梳理 (资源加载) – Flutter APK 文件、图片、字体的加载