Flutter元件學習(二)—— Image
序言
上一節中,我們講了 Flutter
中 Text
元件的一些用法以及 API
,本節我們繼續學習 Flutter
中的 Image
元件,同樣先上圖:
Image元件的構造方法
在 Android
中,我們都知道,圖片的顯示方式有很多,資源圖片、網路圖片、檔案圖片等等,在 Flutter
中也有多種方式,用來載入不同形式的圖片:
- **Image:**通過ImageProvider來載入圖片
- **Image.asset:**用來載入本地資源圖片
- **Image.file:**用來載入本地(File檔案)圖片
- **Image.network:**用來載入網路圖片
- **Image.memory:**用來載入Uint8List資源(byte陣列)圖片
1、Image
Image
的一個引數是 ImageProvider
,基本上所有形式的圖片載入都是依賴它,這個類裡面就是實現圖片載入的原理。用法如下:
new Image(image: new AssetImage('images/logo.png'));
new Image(image: new NetworkImage('http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg'))
複製程式碼
2、Image.asset
載入一個本地資源圖片,和 Android
一樣,有多種解析度的圖片可供選擇,但是沿襲的是 iOS
的圖片風格,分為 1x
,2x
,3x
,具體做法是在專案的根目錄下建立兩個資料夾,如下圖所示:
然後需要在 pubspec.yaml
檔案中宣告一下:
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- images/logo.png
- images/2.0x/logo.png
- images/3.0x/logo.png
複製程式碼
用法如下:
new Image.asset('images/logo.png')
複製程式碼
3、Image.file
載入一個本地 File
圖片,比如相簿中的圖片,用法如下
new Image.file(new File('/storage/xxx/xxx/test.jpg'))
複製程式碼
4、Image.network
載入一個網路圖片,用法如下:
new Image.network('http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg')
複製程式碼
有的時候我們需要像Android那樣使用一個佔位圖或者圖片加載出錯時顯示某張特定的圖片,這時候需要用到 FadeInImage
這個元件:
new FadeInImage.assetNetwork(
placeholder: 'images/logo.png',
image: imageUrl,
width: 120,
fit: BoxFit.fitWidth,
)
new FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageUrl,
width: 120,
fit: BoxFit.fitWidth,
)
複製程式碼
第一種方法是載入一個本地的佔位圖,第二種是載入一個透明的佔位圖,但是需要注意的是,這個元件是不可以設定加載出錯顯示的圖片的;這裡有另一種方法可以使用第三方 package
的 CachedNetworkImage
元件:
new CachedNetworkImage(
width: 120,
fit: BoxFit.fitWidth,
placeholder: new CircularProgressIndicator(),
imageUrl: imageUrl,
errorWidget: new Icon(Icons.error),
)
複製程式碼
CachedNetworkImage
元件中的佔位圖是一個 Widget
,這樣的話就可以自定義了,你想使用什麼樣的元件進行佔位都行,同樣加載出錯的佔位圖也是一個元件,也可以自己定義;該元件也是通過快取來載入圖片的。
5、Image.memory
用來將一個 byte
陣列載入成圖片,用法如下:
new Image.memory(bytes)
複製程式碼
Text元件的API
API名稱 | 功能 |
---|---|
width & height | 用來指定顯示圖片區域的寬高(並非圖片的寬高) |
fit | 設定圖片填充,類似於Android中的ScaleType |
color & colorBlendMode | 這兩個屬性需要配合使用,就是顏色和圖片混合,就類似於Android中的Xfermode |
alignment | 用來控制圖片擺放的位置 |
repeat | 用來設定圖片重複顯示(repeat-x水平重複,repeat-y垂直重複,repeat兩個方向都重複,no-repeat預設情況不重複) |
centerSlice | 設定圖片內部拉伸,相當於在圖片內部設定了一個.9圖,但是需要注意的是,要在顯示圖片的大小大於原圖的情況下才可以使用這個屬性,要不然會報錯 |
matchTextDirection | 這個需要配合Directionality進行使用 |
gaplessPlayback | 當圖片發生改變之後,重新載入圖片過程中的樣式(1、原圖片保留) |
fit
屬性中有很多值可以設定:
屬性名稱 | 樣式 |
---|---|
BoxFit.contain | 全圖居中顯示但不充滿,顯示原比例 |
BoxFit.cover | 圖片可能拉伸,也可能裁剪,但是充滿容器 |
BoxFit.fill | 全圖顯示且填充滿,圖片可能會拉伸 |
BoxFit.fitHeight | 圖片可能拉伸,可能裁剪,高度充滿 |
BoxFit.fitWidth | 圖片可能拉伸,可能裁剪,寬度充滿 |
BoxFit.scaleDown | 效果和contain差不多, 但是隻能縮小圖片,不能放大圖片 |
colorBlendMode
屬性中有很多值可以設定,由於可選值太多,這裡就不一一介紹了,有興趣的可以去官網colorBlendMode屬性介紹看看
實現圓角/圓形圖片
1、圓角
很多時候我們需要給圖片設定圓角,那麼在flutter中是怎麼實現的呢?有很多種方法可以實現,下面我舉兩個例子:
使用裁剪來實現圖片圓角:
new ClipRRect(
child: Image.network(
imageUrl,
scale: 8.5,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
)
使用邊框來實現圖片圓角:
new Container(
width: 120,
height: 60,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(10.0),
image: DecorationImage(
image: NetworkImage(imageUrl),
fit: BoxFit.cover),
),
)
複製程式碼
需要注意的是,使用邊框實現的時候要注意設定 fit
屬性,不然效果也是有問題的,當然了你還可以使用 Material
元件來實現,這個大家可以自己去嘗試。
2、圓形
圓形圖片用得最多的應該是頭像之類的,這種同樣有多種方式可以實現,下面我也舉兩個例子:
使用裁剪實現圓形圖片:
new ClipOval(
child: Image.network(
imageUrl,
scale: 8.5,
),
)
使用CircleAvatar來實現圓形圖片:
new CircleAvatar(
backgroundImage: NetworkImage(imageUrl),
radius: 50.0,
)
複製程式碼
當然了,你還可以使用邊框 BoxDecoration
來實現,效果也是一樣的。
下面來看一下詳細的程式碼實現:
class _ImageViewWidget extends State<ImageViewWidget> {
var imageUrl =
"http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg";
var imageUrl2 =
"http://n.sinaimg.cn/sports/2_img/upload/4f160954/107/w1024h683/20181128/Yrxn-hpinrya6814381.jpg";
@override
Widget build(BuildContext context) {
return new Align(
child: ListView(
children: <Widget>[
new Text('資源圖片:'),
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Padding(
padding: const EdgeInsets.all(10.0),
child: Image.asset(
'images/logo.png',
),
),
// new Image.file(
// File('/storage/emulated/0/Download/test.jpg'),
// width: 120,
// //fill(全圖顯示且填充滿,圖片可能會拉伸),contain(全圖顯示但不充滿,顯示原比例),cover(顯示可能拉伸,也可能裁剪,充滿)
// //fitWidth(顯示可能拉伸,可能裁剪,寬度充滿),fitHeight顯示可能拉伸,可能裁剪,高度充滿),scaleDown(效果和contain差不多,但是)
// ),
],
),
new Text('網路佔位圖片CachedNetworkImage:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.network(
imageUrl,
scale: 8.5,
),
new Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: CachedNetworkImage(
width: 120,
fit: BoxFit.fitWidth,
placeholder: Image(image: AssetImage('images/logo.png')),
imageUrl: imageUrl,
errorWidget: new Icon(Icons.error),
),
),
new CachedNetworkImage(
imageUrl: imageUrl,
width: 120,
fit: BoxFit.fitWidth,
placeholder: CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
)
],
),
),
new Text('網路佔位圖片FadeInImage:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: new Row(
children: <Widget>[
new FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: imageUrl,
width: 120,
fit: BoxFit.fitWidth,
),
new Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: new FadeInImage.assetNetwork(
placeholder: 'images/logo.png',
image: imageUrl,
width: 120,
fit: BoxFit.fitWidth,
),
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
),
new Text('圓形圓角圖片:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new ClipOval(
child: Image.network(
imageUrl,
width: 100,
height: 100,
fit: BoxFit.fitHeight,
),
),
new Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: ClipOval(
child: Image.asset(
'images/logo.png',
width: 100,
height: 100,
fit: BoxFit.fitHeight,
),
),
),
new ClipRRect(
child: Image.network(
imageUrl,
scale: 8.5,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
)
],
),
),
new Text('顏色混合圖片:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Image.asset(
'images/logo.png',
color: Colors.red,
colorBlendMode: BlendMode.darken,
),
new Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: Image.network(
imageUrl,
scale: 8.5,
colorBlendMode: BlendMode.colorDodge,
color: Colors.blue,
),
),
],
),
),
new Text('centerSlice圖片內部拉伸:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: new Image.asset(
'images/logo.png',
width: 250,
height: 250,
fit: BoxFit.contain,
centerSlice:
new Rect.fromCircle(center: const Offset(20, 20), radius: 1),
),
),
new Text('matchTextDirection圖片內部方向'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Directionality(
textDirection: TextDirection.ltr,
child: Image.network(
imageUrl,
height: 100,
matchTextDirection: true,
fit: BoxFit.fitHeight,
),
),
new Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: Directionality(
textDirection: TextDirection.rtl,
child: Image.network(
imageUrl,
height: 100,
matchTextDirection: true,
fit: BoxFit.fitHeight,
),
),
),
],
),
),
new Text('點選替換圖片'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: () {
setState(() {
widget.networkImage =
new NetworkImage(imageUrl2, scale: 8.5);
});
},
child: Text('點選更換圖片'),
),
new Image(
gaplessPlayback: false,
fit: BoxFit.contain,
image: widget.networkImage,
),
],
),
)
],
),
);
}
}
複製程式碼
程式碼已上傳至Github
公眾號
歡迎關注我的個人公眾號【IT先森養成記】,專注大前端技術分享,包含Android,Java,Kotlin,Flutter,HTML,CSS,JS等技術;在這裡你能得到的不止是技術上的提升,還有一些學習經驗以及志同道合的朋友,趕快加入我們,一起學習,一起進化吧!!!