# 資源管理
Flutter應用程序可以包含代碼和 assets(有時稱為資源)。assets是會打包到程序安裝包中的,可在運行時訪問。常見類型的assets包括靜態數據(例如JSON文件)、配置文件、圖標和圖片(JPEG,WebP,GIF,動畫WebP / GIF,PNG,BMP和WBMP)等。
## 指定 assets
和包管理一樣,Flutter也使用[`pubspec.yaml`](https://www.dartlang.org/tools/pub/pubspec)文件來管理應用程序所需的資源。舉一個例子:
```
flutter:
assets:
- assets/my_icon.png
- assets/background.png
```
`assets`指定應包含在應用程序中的文件。 每個asset都通過相對于`pubspec.yaml`文件所在位置的顯式路徑進行標識。asset的聲明順序是無關緊要的。asset的實際目錄可以是任意文件夾(在本示例中是assets)。
在構建期間,Flutter將asset放置到稱為 *asset bundle* 的特殊存檔中,應用程序可以在運行時讀取它們(但不能修改)。
## Asset 變體(variant)
構建過程支持asset變體的概念:不同版本的asset可能會顯示在不同的上下文中。 在`pubspec.yaml`的assets部分中指定asset路徑時,構建過程中,會在相鄰子目錄中查找具有相同名稱的任何文件。這些文件隨后會與指定的asset一起被包含在asset bundle中。
例如,如果應用程序目錄中有以下文件:
- …/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)。
在選擇匹配當前設備分辨率的圖片時,Flutter會使用到asset變體(見下文),將來,Flutter可能會將這種機制擴展到本地化、閱讀提示等方面。
## 加載 assets
您的應用可以通過[`AssetBundle`](https://docs.flutter.io/flutter/services/AssetBundle-class.html)對象訪問其asset 。有兩種主要方法允許從Asset bundle中加載字符串或圖片(二進制)文件。
### 加載文本assets
- 通過[`rootBundle`](https://docs.flutter.io/flutter/services/rootBundle.html) 對象加載:每個Flutter應用程序都有一個[`rootBundle`](https://docs.flutter.io/flutter/services/rootBundle.html)對象, 通過它可以輕松訪問主資源包,直接使用`package:flutter/services.dart`中全局靜態的`rootBundle`對象來加載asset即可。
- 通過 [`DefaultAssetBundle`](https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html) 加載:建議使用 [`DefaultAssetBundle`](https://docs.flutter.io/flutter/widgets/DefaultAssetBundle-class.html) 來獲取當前BuildContext的AssetBundle。 這種方法不是使用應用程序構建的默認asset bundle,而是使父級widget在運行時動態替換的不同的AssetBundle,這對于本地化或測試場景很有用。
通常,可以使用`DefaultAssetBundle.of()`在應用運行時來間接加載asset(例如JSON文件),而在widget上下文之外,或其它`AssetBundle`句柄不可用時,可以使用`rootBundle`直接加載這些asset,例如:
```
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
Future<String> loadAsset() async {
return await rootBundle.loadString('assets/config.json');
}
```
### 加載圖片
類似于原生開發,Flutter也可以為當前設備加載適合其分辨率的圖像。
#### 聲明分辨率相關的圖片 assets
[`AssetImage`](https://docs.flutter.io/flutter/painting/AssetImage-class.html) 可以將asset的請求邏輯映射到最接近當前設備像素比例(dpi)的asset。為了使這種映射起作用,必須根據特定的目錄結構來保存asset:
- …/image.png
- …/**M**x/image.png
- …/**N**x/image.png
- …etc.
其中M和N是數字標識符,對應于其中包含的圖像的分辨率,也就是說,它們指定不同設備像素比例的圖片。
主資源默認對應于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上指定渲染圖像的寬度和高度,那么`Image` widget將占用與主資源相同的屏幕空間大小。 也就是說,如果`.../my_icon.png`是72px乘72px,那么`.../3.0x/my_icon.png`應該是216px乘216px; 但如果未指定寬度和高度,它們都將渲染為72像素×72像素(以邏輯像素為單位)。
`pubspec.yaml`中asset部分中的每一項都應與實際文件相對應,但主資源項除外。當主資源缺少某個資源時,會按分辨率從低到高的順序去選擇 ,也就是說1x中沒有的話會在2x中找,2x中還沒有的話就在3x中找。
#### 加載圖片
要加載圖片,可以使用 [`AssetImage`](https://docs.flutter.io/flutter/painting/AssetImage-class.html)類。例如,我們可以從上面的asset聲明中加載背景圖片:
```
Widget build(BuildContext context) {
return new DecoratedBox(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('graphics/background.png'),
),
),
);
}
```
注意,`AssetImage` 并非是一個widget, 它實際上是一個`ImageProvider`,有些時候你可能期望直接得到一個顯示圖片的widget,那么你可以使用`Image.asset()`方法,如:
```
Widget build(BuildContext context) {
return Image.asset('graphics/background.png');
}
```
使用默認的 asset bundle 加載資源時,內部會自動處理分辨率等,這些處理對開發者來說是無感知的。 (如果使用一些更低級別的類,如 [`ImageStream`](https://docs.flutter.io/flutter/painting/ImageStream-class.html)或 [`ImageCache`](https://docs.flutter.io/flutter/painting/ImageCache-class.html) 時你會注意到有與縮放相關的參數)
#### 依賴包中的資源圖片
要加載依賴包中的圖像,必須給`AssetImage`提供`package`參數。
例如,假設您的應用程序依賴于一個名為“my\_icons”的包,它具有如下目錄結構:
- …/pubspec.yaml
- …/icons/heart.png
- …/icons/1.5x/heart.png
- …/icons/2.0x/heart.png
- …etc.
然后加載圖像,使用:
```
new AssetImage('icons/heart.png', package: 'my_icons')
```
或
```
new Image.asset('icons/heart.png', package: 'my_icons')
```
**注意:包在使用本身的資源時也應該加上`package`參數來獲取**。
##### 打包包中的 assets
如果在`pubspec.yaml`文件中聲明了期望的資源,它將會打包到相應的package中。特別是,包本身使用的資源必須在`pubspec.yaml`中指定。
包也可以選擇在其`lib/`文件夾中包含未在其`pubspec.yaml`文件中聲明的資源。在這種情況下,對于要打包的圖片,應用程序必須在`pubspec.yaml`中指定包含哪些圖像。 例如,一個名為“fancy\_backgrounds”的包,可能包含以下文件:
- …/lib/backgrounds/background1.png
- …/lib/backgrounds/background2.png
- …/lib/backgrounds/background3.png
要包含第一張圖像,必須在`pubspec.yaml`的assets部分中聲明它:
```
flutter:
assets:
- packages/fancy_backgrounds/backgrounds/background1.png
```
`lib/`是隱含的,所以它不應該包含在資產路徑中。
### 特定平臺 assets
上面的資源都是flutter應用中的,這些資源只有在Flutter框架運行之后才能使用,如果要給我們的應用設置APP圖標或者添加啟動圖,那我們必須使用特定平臺的assets。
#### 設置APP圖標
更新Flutter應用程序啟動圖標的方式與在本機Android或iOS應用程序中更新啟動圖標的方式相同。
- Android
在Flutter項目的根目錄中,導航到`.../android/app/src/main/res`目錄,里面包含了各種資源文件夾(如`mipmap-hdpi`已包含占位符圖像”ic\_launcher.png”)。 只需按照[Android開發人員指南](https://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html#size)中的說明, 將其替換為所需的資源,并遵守每種屏幕密度(dpi)的建議圖標大小標準。

> **注意:** 如果您重命名.png文件,則還必須在您`AndroidManifest.xml`的`<application>`標簽的`android:icon`屬性中更新名稱。
- iOS
在Flutter項目的根目錄中,導航到`.../ios/Runner`。該目錄中`Assets.xcassets/AppIcon.appiconset`已經包含占位符圖片。 只需將它們替換為適當大小的圖片。保留原始文件名稱。

#### 更新啟動頁

在Flutter框架加載時,Flutter會使用本地平臺機制繪制啟動頁。此啟動頁將持續到Flutter渲染應用程序的第一幀時。
> **注意:** 這意味著如果您不在應用程序的`main()`方法中調用[runApp](https://docs.flutter.io/flutter/widgets/runApp.html) 函數 (或者更具體地說,如果您不調用[`window.render`](https://docs.flutter.io/flutter/dart-ui/Window/render.html)去響應[`window.onDrawFrame`](https://docs.flutter.io/flutter/dart-ui/Window/onDrawFrame.html))的話, 啟動屏幕將永遠持續顯示。
##### Android
要將啟動屏幕(splash screen)添加到您的Flutter應用程序, 請導航至`.../android/app/src/main`。在`res/drawable/launch_background.xml`,通過自定義drawable來實現自定義啟動界面(你也可以直接換一張圖片)。
##### iOS
要將圖片添加到啟動屏幕(splash screen)的中心,請導航至`.../ios/Runner`。在`Assets.xcassets/LaunchImage.imageset`, 拖入圖片,并命名為`LaunchImage.png`、`LaunchImage@2x.png`、`LaunchImage@3x.png`。 如果你使用不同的文件名,那您還必須更新同一目錄中的`Contents.json`文件,圖片的具體尺寸可以查看蘋果官方的標準。
您也可以通過打開Xcode完全自定義storyboard。在Project Navigator中導航到`Runner/Runner`然后通過打開`Assets.xcassets`拖入圖片,或者通過在LaunchScreen.storyboard中使用Interface Builder進行自定義。

- 緣起
- 起步
- 移動開發技術簡介
- Flutter簡介
- 搭建Flutter開發環境
- 常見配置問題
- Dart語言簡介
- 第一個Flutter應用
- 計數器示例
- 路由管理
- 包管理
- 資源管理
- 調試Flutter APP
- Dart線程模型及異常捕獲
- 基礎Widgets
- Widget簡介
- 文本、字體樣式
- 按鈕
- 圖片和Icon
- 單選框和復選框
- 輸入框和表單
- 布局類Widgets
- 布局類Widgets簡介
- 線性布局Row、Column
- 彈性布局Flex
- 流式布局Wrap、Flow
- 層疊布局Stack、Positioned
- 容器類Widgets
- Padding
- 布局限制類容器ConstrainedBox、SizeBox
- 裝飾容器DecoratedBox
- 變換Transform
- Container容器
- Scaffold、TabBar、底部導航
- 可滾動Widgets
- 可滾動Widgets簡介
- SingleChildScrollView
- ListView
- GridView
- CustomScrollView
- 滾動監聽及控制ScrollController
- 功能型Widgets
- 導航返回攔截-WillPopScope
- 數據共享-InheritedWidget
- 主題-Theme
- 事件處理與通知
- 原始指針事件處理
- 手勢識別
- 全局事件總線
- 通知Notification
- 動畫
- Flutter動畫簡介
- 動畫結構
- 自定義路由過渡動畫
- Hero動畫
- 交錯動畫
- 自定義Widget
- 自定義Widget方法簡介
- 通過組合現有Widget實現
- 實例:TurnBox
- CustomPaint與Canvas
- 實例:圓形漸變進度條(自繪)
- 文件操作與網絡請求
- 文件操作
- Http請求-HttpClient
- Http請求-Dio package
- 實例:Http分塊下載
- WebSocket
- 使用Socket API
- Json轉Model
- 包與插件
- 開發package
- 插件開發:平臺通道簡介
- 插件開發:實現Android端API
- 插件開發:實現IOS端API
- 系統能力調用
- 國際化
- 讓App支持多語言
- 實現Localizations
- 使用Intl包
- Flutter核心原理
- Flutter UI系統
- Element和BuildContext
- RenderObject與RenderBox
- Flutter從啟動到顯示