初略講解Flutter的資源管理

zane發表於2019-06-26

Flutter應用程式可以包含程式碼和資源(assets)。assets是會打包到程式安裝包中的,可在執行時訪問。常見型別的assets包括靜態資料(如json檔案)、配置檔案以及圖示和圖片(JPEG、WebP、GIF、動畫WebP/GIF、PNG、BMP、WBMP)等。

一、指定assets

和上一節講解包管理一樣,Flutter也是使用pubspec.yaml檔案來管理應用程式所需的資源,例如:

flutter:
    assets:
        - images/my_icon.png
        - images/background.png
複製程式碼

針對上述程式碼,我們大致可以瞭解到:

  • assets指定應包含應用程式中的檔案;
  • 每個asset都通過相對於pubspec.yaml檔案所在位置的顯式路徑進行標識;
  • asset的宣告順序是無關緊要的;
  • asset的實際目錄可以是任意資料夾(在本例中是images)

二、asset變體(variant)

應用程式構建過程支援asset變體的概念:不同版本的asset可能會顯示在不同的上下文中。也就是說:假如在pubspec.yaml檔案中我們只指定所用到的asset的路徑,但是在應用程式構建過程中,會在相鄰子目錄中查詢具有相同名稱的任何檔案,這些檔案隨後會與指定的asset一起被包含在asset bundle中。

例如,應用程式目錄中有以下檔案:

  • .../pubspec.yaml
  • .../graphics/my_icon.png
  • .../graphics/background.png
  • .../graphics/dark/background.png
  • ...etc.

上述我們可以發現,有兩個graphics/background.pnggraphics/dark/background.png名稱相同但路徑不同的圖片檔案,接下來我們在pubspec.yaml檔案中只指定:

flutter:
    assets:
        - graphics/background.png
複製程式碼

那麼這兩個graphics/background.pnggraphics/dark/background.png圖片檔案,都會在應用程式構建中包含到asset bundle中。前者被認為main asset(主資源),後者被認為是一種變體(variant)。

在選擇匹配當前裝置解析度的圖片時,Flutter會使用到asset變體,將來Flutter可能會將這種機制擴充套件到本地化、閱讀提示等方面。

三、載入assets

應用程式可以通過AssetBundle物件訪問其asset,有兩種主要方法允許從asset bundle中載入字串或圖片(二進位制)檔案。

1、載入文字assets

  • 通過rootBundle物件載入:每個Flutter應用程式都有一個rootBundle物件,通過它可以輕鬆訪問主資源包,直接使用package:flutter/services.dart中全域性靜態的rootBundle物件來載入asset即可。
  • 通過DefaultAssetBundle載入:建議使用DefaultAssetBundle來獲取當前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');
}
複製程式碼

2、載入圖片assets

類似於原生開發,Flutter也可以為當前裝置載入適合其解析度的影象。

①、宣告解析度相關的圖片assets

AssetImage可以將asset的請求邏輯對映到最接近當前裝置畫素比例(dpi)的asset,為了使這種對映起作用,必須根據特定的目錄結構來儲存asset:

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

其中M和N是數字識別符號,對應於其中包含的影象的解析度,也就是說,M和N分別指定不同裝置畫素比例的圖片,例如:

  • .../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將被選擇;而我們在上面提到的main asset(主資源),它預設對應於1.0倍的解析度圖片,也就是.../my_icon.png將被選擇。

因此在pubspec.yaml檔案下指定asset區域中的每一項都應與實際檔案相對應,但main asset(主資源)項除外,因為當主資源缺少某個資源時,會按解析度從低到高的順序去選擇,也就說1x中沒有的話會在2x中找,2x中沒有的話就到3x中找。

值得一提的是,如果未在ImageWidget上指定渲染影象的寬度和高度,那麼ImageWidget將佔用與主資源相同的螢幕空間大小。也就是說,如果.../my_icon.png是72px乘72px,那麼.../3.0x/my_icon.png應該是216px乘216px;但如果未給ImageWidget指定高度和寬度,它們都將渲染為72px乘72px。

②、載入本地資源圖片

要載入本地圖片,可以使用AssetImage類。例如,我們可以從上面的asset宣告中載入背景圖片:

Widget build(BuildContext context) {
  return new DecoratedBox(
    decoration: new BoxDecoration(
      image: new DecorationImage(
        image: new AssetImage('graphics/background.png'),
      ),
    ),
  );
}
複製程式碼

注意,AssetImage並非是一個Widget,它實際上是一個ImageProvider;不過你可以使用Image.asset()方法,直接得到一個顯示圖片的Widget,例如:

Widget build(BuildContext context) {
  return Image.asset('graphics/background.png');
}
複製程式碼

使用預設的asset bundle載入資源時,內部會自動處理解析度等,這些處理對開發者來說是無感知的(如果使用一些更低階別的類,如ImageStreamImageCache時你會注意到有與縮放相關的引數)。

③、載入依賴包中的資源圖片

要載入依賴包中的圖片,必須給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引數來獲取。

1)、打包包中的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開發人員指南中的說明,將其替換為所需的資源,並遵守每種螢幕密度(dpi)的建議圖示大小標準。

    初略講解Flutter的資源管理

    注意:如果您重新命名.png檔案,則還需要在您AndroidManifest.xml<application>標籤的android:icon屬性中更新名稱。

  • iOS

    在Flutter專案的根目錄中,導航到.../ios/Runner/Assets.xcassets/AppIcon.appiconset目錄。該目錄中已包含佔位符圖片,只需將它們替換為適當大小的圖片,保留原始檔名稱。

    初略講解Flutter的資源管理

新增啟動圖

在Flutter框架載入時,Flutter會使用本地平臺機制繪製啟動頁,此啟動頁將持續到Flutter渲染應用程式的第一幀時。

注意:這意味著如果你不在應用程式的main()方法中呼叫runApp()函式(或者更具體地說,如果你不呼叫window.render去響應window.onDrawFrame)的話,啟動頁將永遠持續顯示。

  • Android

    要將啟動螢幕(splash screen)新增到你的Flutter應用程式,請導航到.../android/app/src/main/res/drawable目錄下,在launch_background.xml 檔案中,通過自定義drawable來實現自定義啟動介面(也可以直接換一張圖片)。

    初略講解Flutter的資源管理

  • iOS

    要將圖片新增到啟動螢幕(splash screen)的中心,請導航到.../ios/Runner/Assets.xcassets/LaunchImage.imageset目錄下,拖入圖片並命名為LaunchImage.pngLaunchImage@2x.pngLaunchImage@3x.png。如果你使用不同的檔名,那你還需要更新同一目錄中的Contents.json檔案,圖片的具體尺寸可以檢視蘋果官方的標準。

    您也可以通過開啟Xcode完全自定義storyboard。在Project Navigator中導航到Runner/Runner/Assets.xcassets目錄中,拖入圖片,或者通過在LaunchScreen.storyboard中使用Interface Builder進行自定義。

    初略講解Flutter的資源管理

相關文章