Flutter 可以包含圖片、組態檔、JSON 檔等相關資源,除了在專案中包含資源之外,必須在 pubspec.yaml 加入 assets:
設定:
...
flutter:
uses-material-design: true
assets:
- images/caterpillar.png
路徑是相對於 pubspec.yaml,在上例中指定了 images/caterpillar.png
,在打包資源時,其實會遞廻地將 images 中的每個 caterpillar.png 都包進去,也就是說,若實際上 images 資料夾中有 images/2.0x/caterpillar.png、images/3.0x/caterpillar.png 等,會全部打包進去,這表示若你想為不同解析度準備不同大小的影像時,不用在 pubspec.yaml 中逐一設定。
如果要指定整個資料夾中的資源,路徑最後以 /
結尾,例如 images/
。
想載入圖片資源最簡單的方式,是透過 Image.asset
,例如:
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(
'openhome.cc'
),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Image.asset('images/caterpillar.png'),
),
Text('我是一隻弱小的毛毛蟲,想像有天可以成為強壯的挖土機,擁有挖掘夢想的神奇手套!')
],
),
),
)
);
這會產生以下的畫面:
Image.asset
其實是個建構式,會建立 Image
實例,被指定字串會用來建立 AssetImage
實例,它會根據 devicePixelRatio
特性,挑選 assets 中適當解析度的圖片,規則可參考〈Declaring resolution-aware image assets〉。
你也可以自行建立 Image
、AssetImage
:
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(
'openhome.cc'
),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
// 自行建立 Image、AssetImage
child: Image(
image: AssetImage('images/caterpillar.png'),
width: 250,
height: 250,
),
),
Text('我是一隻弱小的毛毛蟲,想像有天可以成為強壯的挖土機,擁有挖掘夢想的神奇手套!')
],
),
),
)
);
AssetImage
實例會從 AssetBundle
取得圖片,真正管理資源的是 AssetBundle
,與資源載入相關的方法有 load
、loadString
、loadStructuredData
,這些 load 方法都是非同步,load
用來載入位元資源(傳回 Future<ByteData>
),loadString
用來載入文字資源(傳回 Future<String>
),loadStructuredData
基於 loadString
,需要指定轉換函式,函式接受字串,執行後傳回結構化資料型態的實例(傳回型態 Future<T>
)。
來看看 loadString
的簡單使用,要載入資源中的文字檔,同樣要在加入 assets:
設定:
...
flutter:
uses-material-design: true
assets:
- images/caterpillar.png
- messages/caterpillar.txt
每個 Flutter App 都會有個 rootBundle
,若要使用,可以 import 'package:flutter/services.dart'
中的 rootBundle
,例如:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
不過 rootBundle
代表著整個 App,通常會透過 DefaultAssetBundle
,根據給定的 BuildContext
來載入資源,如此之來,父 Widget
會有機會在執行時期指定 AssetBundle
,以載入不同的資源。
底下的範例,圖片下的文字來自於文字檔:
import 'package:flutter/material.dart';
void main() => runApp(
MyApp()
);
Future loadString(BuildContext context) async {
return await DefaultAssetBundle.of(context)
.loadString('messages/caterpillar.txt');
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _msg = '訊息載入中…';
@override
void initState() {
super.initState();
loadString(context).then((msg) {
setState(() {
_msg = msg;
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(
'openhome.cc'
),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Image.asset(
'images/caterpillar.png'
),
),
Text(_msg)
],
),
),
);
}
}
因為 loadString
方法是非同步地,為了能在資源載入後能通知 Flutter 框架,繼承了 StatefulWidget
來定義元件,Flutter 框架會在建立 State
實例後,呼叫 initState
進行狀態的初始,因此 MyApp
對應的狀態物件 _MyAppState
重新定義了 initState
,在其中載入文字資源檔,並在載入後呼叫了 setState
方法,通知狀態已改變,這時就會重新建構,顯示載入後的訊息。