【Flutter實戰】圖片元件及四大案例
阿新 • • 發佈:2020-06-17
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211503680-942638115.png)
> 老孟導讀:大家好,這是【Flutter實戰】系列文章的第三篇,這一篇講解圖片元件,Image有很多高階用法,希望對您有所幫助。
圖片元件是Flutter基礎元件之一,和文字元件一樣必不可少。圖片元件包含Image和Icon兩個元件,本質上Icon不屬於圖片元件,但其外形效果上類似於圖片。
在專案中建議優先使用Icon元件,Icon本質上是一種字型,只不過顯示的不是**文字**,而是圖示,而Image元件先通過圖片解碼器將圖片解碼,所以Icon有如下優點:
- 通常情況下,圖示比圖片體積更小,顯著的減少App包體積。
- 圖示不會出現失真或者模糊的現象,例如將20x20的圖片,渲染在200x200的螢幕上,圖片會失真或模糊,而圖示是向量圖,不會失真,就像字型一樣。
- 多個圖示可以存放在一個檔案中,方便管理。
- 全平臺通用。
### Image
Image元件用於顯示圖片,圖片的來源可以是網路、專案中圖片或者裝置上的圖片。
載入網路圖片:
```dart
Image.network(
'http://pic1.win4000.com/pic/c/cf/cdc983699c.jpg',
)
```
載入專案中圖片:
首先將圖片拷貝到專案中,通常情況下,拷貝到`assets/images/`目錄下,`assets/images/`目錄為手動建立,新建的專案預設是沒有此目錄的。
設定`pubspec.yaml`配置檔案:
```dart
assets:
- assets/images/
```
或者指定具體圖片的名稱:
```dart
assets:
- assets/images/aa.jpg
```
通常情況下,使用第一種方式,因為圖片會有很多張,增加一張就這裡配置一個太麻煩。
**注意:assets前面的空格問題,極容易引發編譯異常,正確格式如下:**
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211504533-1465883070.png)
載入圖片:
```dart
Image.asset('assets/images/aa.jpg')
```
載入裝置上的圖片:
要載入裝置(手機)上的圖片首先需要獲取裝置圖片的路徑,由於不同平臺的路徑不同,因此路徑的獲取必須依靠原生支援,如果瞭解原生(Android和iOS)開發,可以直接使用**MethodChannel**獲取路徑,如果不懂原生(Android和iOS)開發,可以使用第三方外掛獲取路徑,這裡推薦**官方的[path_provider](https://pub.flutter-io.cn/packages/path_provider)**。
載入裝置上的圖片:
```dart
Image.file(File('path'))
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211505602-901262895.webp)
設定圖片的大小:
```dart
Image.asset('assets/images/aa.jpg',width: 100,height: 200,),
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211505937-1003636984.webp)
當Image的大小和圖片大小不匹配時,需要設定填充模式`fit`,設定元件大小為150x150,
```dart
Container(
color: Colors.red.withOpacity(.3),
child: Image.asset('assets/images/aa.jpg',width: 150,height: 150),
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211506338-1578456160.webp)
看到,圖片左右兩邊有空白區域(淺紅色填充的區域),如果想要圖片充滿整個區域,設定如下:
```dart
Container(
color: Colors.red.withOpacity(.3),
child: Image.asset('assets/images/aa.jpg',width: 150,height: 150,fit: BoxFit.fill,),
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211506678-1925594025.webp)
雖然圖片充滿整個區域,但圖片變形了,使圖片等比拉伸,直到兩邊都充滿區域:
```dart
Container(
color: Colors.red.withOpacity(.3),
child: Image.asset('assets/images/aa.jpg',width: 150,height: 150,fit: BoxFit.cover,),
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211507100-1734169090.webp)
此時,圖片未變形且兩邊都充滿區域,不過圖片被裁減了一部分。
`fit`引數就是設定填充方式,其值介紹如下:
- fill:完全填充,寬高比可能會變。
- contain:等比拉伸,直到一邊填充滿。
- cover:等比拉伸,直到2邊都填充滿,此時一邊可能超出範圍。
- fitWidth:等比拉伸,寬填充滿。
- fitHeight:等比拉伸,高填充滿。
- none:當元件比圖片小時,不拉伸,超出範圍擷取。
- scaleDown:當元件比圖片小時,圖片等比縮小,效果和**contain**一樣。
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211507629-605854378.webp)
`BoxFit.none`的裁減和`alignment`相關,預設居中,
```dart
Image.asset(
'assets/images/aa.jpg',
width: 150,
height: 150,
fit: BoxFit.none,
alignment: Alignment.centerRight,
),
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211508043-290416911.webp)
左邊為原圖。
設定對齊方式:
```dart
Container(
color: Colors.red.withOpacity(.3),
child: Image.asset(
'assets/images/aa.jpg',
width: 150,
height: 150,
alignment: Alignment.centerLeft,
),
),
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211509395-967971942.webp)
`color`和`colorBlendMode`用於將顏色和圖片進行顏色混合,`colorBlendMode`表示混合模式,下面介紹的混合模式比較多,瀏覽一遍即可,此屬性可以用於簡單的濾鏡效果。
- clear:清楚源影象和目標影象。
- color:獲取源影象的色相和飽和度以及目標影象的光度。
- colorBurn:將目標的倒數除以源,然後將結果倒數。
- colorDodge:將目標除以源的倒數。
- darken:通過從每個顏色通道中選擇最小值來合成源影象和目標影象。
- difference:從每個通道的較大值中減去較小的值。合成黑色沒有效果。合成白色會使另一張影象的顏色反轉。
- dst:僅繪製目標影象。
- dstATop:將目標影象合成到源影象上,但僅在與源影象重疊的位置合成。
- dstIn:顯示目標影象,但僅顯示兩個影象重疊的位置。不渲染源影象,僅將其視為蒙版。源的顏色通道將被忽略,只有不透明度才起作用。
- dstOut:顯示目標影象,但僅顯示兩個影象不重疊的位置。不渲染源影象,僅將其視為蒙版。源的顏色通道將被忽略,只有不透明度才起作用。
- dstOver:將源影象合成到目標影象下。
- exclusion:從兩個影象的總和中減去兩個影象的乘積的兩倍。
- hardLight:調整源影象和目標影象的成分以使其適合源影象之後,將它們相乘。
- hue:獲取源影象的色相,以及目標影象的飽和度和光度。
- lighten:通過從每個顏色通道中選擇最大值來合成源影象和目標影象。
- luminosity:獲取源影象的亮度,以及目標影象的色相和飽和度。
- modulate:將源影象和目標影象的顏色分量相乘。
- multiply:將源影象和目標影象的分量相乘,包括alpha通道。
- overlay:調整源影象和目標影象的分量以使其適合目標後,將它們相乘。
- plus:對源影象和目標影象的組成部分求和。
- saturation:獲取源影象的飽和度以及目標影象的色相和亮度。
- screen:將源影象和目標影象的分量的逆值相乘,然後對結果求逆。
- softLight:對於低於0.5的源值使用colorDodge,對於高於0.5的源值使用colorBurn。
- src:放置目標影象,僅繪製源影象。
- srcATop:將源影象合成到目標影象上,但僅在與目標影象重疊的位置合成。
- srcIn:顯示源影象,但僅顯示兩個影象重疊的位置。目標影象未渲染,僅被視為蒙版。目標的顏色通道將被忽略,只有不透明度才起作用。
- srcOut:顯示源影象,但僅顯示兩個影象不重疊的位置。
- srcOver:將源影象合成到目標影象上。
- xor:將按位異或運算子應用於源影象和目標影象。
**是不是感覺看了和沒看差不多,看了也看不懂。**正常,估計只有學過視覺演算法的才能看懂吧,直接看下各個屬性的效果吧:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211514120-653025584.webp)
`repeat`表示當元件有空餘位置時,將會重複顯示圖片
```dart
Image.asset(
'assets/images/aa.jpg',
width: double.infinity,
height: 150,
repeat: ImageRepeat.repeatX,
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211514578-1370896991.webp)
重複的模式有:
- repeat:x,y方向都充滿。
- repeatX:x方向充滿。
- repeatY:y方向充滿。
- noRepeat:不重複。
`matchTextDirection`設定為true時,圖片的繪製方向為**TextDirection**設定的方向,其父元件必須為**Directionality**:
```dart
Directionality(
textDirection: TextDirection.rtl,
child: Image.asset(
'assets/images/logo.png',
height: 150,
matchTextDirection: true,
)),
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211515035-610895681.png)
左邊為原圖,效果是左右映象。
`filterQuality`表示繪製圖像的質量,從高到低為:high->medium->low->none。越高效果越好,越平滑,當然效能損耗越大,預設是`low`,如果發現圖片有鋸齒,可以設定此引數。
當載入圖片的時候回撥`frameBuilder`,當此引數為null時,此控制元件將會在圖片載入完成後顯示,未載入完成時顯示空白,尤其在載入網路圖片時會更明顯。因此此引數可以用於處理圖片載入時顯示佔位圖片和載入圖片的過渡效果,比如淡入淡出效果。
下面的案例是淡入淡出效果:
```dart
Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/puffin.jpg',
frameBuilder: (BuildContext context, Widget child, int frame,
bool wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) {
return child;
}
return AnimatedOpacity(
child: child,
opacity: frame == null ? 0 : 1,
duration: const Duration(seconds: 2),
curve: Curves.easeOut,
);
},
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211518684-816893946.gif)
`loadingBuilder`引數比`frameBuilder`控制的力度更細,可以獲取圖片載入的進度,下面的案例顯示了載入進度條:
```dart
Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/puffin.jpg',
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes
: null,
),
);
})
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211519249-1102820399.gif)
`centerSlice`用於.9圖,.9圖用於拉伸圖片的特定區域,`centerSlice`設定的區域(Rect)就是拉伸的區域。.9圖通常用於控制元件大小、寬高比不固定的場景,比如**聊天背景圖片**等。
```dart
Container(
width: 250,
height: 300,
decoration: BoxDecoration(
image: DecorationImage(
centerSlice: Rect.fromLTWH(20, 20, 10, 10),
image: AssetImage(
'assets/images/abc.jpg',
),
fit: BoxFit.fill))),
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211520400-238304079.webp)
上面為原圖,下面為拉伸的圖片。
在使用時大概率會出現如下異常:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211520724-2127535600.png)
這是由於圖片比元件的尺寸大,如果使用`centerSlice`屬性,圖片必須比元件的尺寸小,一般情況下,.9圖的尺寸都非常小。
### Icon
Icon是圖示元件,Icon不具有互動屬性,如果想要互動,可以使用IconButton。
```dart
Icon(Icons.add),
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211521423-741740007.webp)
設定圖示的大小和顏色:
```dart
Icon(
Icons.add,
size: 40,
color: Colors.red,
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211521643-627854399.webp)
上面的黑色為預設大小和顏色。
`Icons.add`是系統提供的圖示,建立Flutter專案的時候,`pubspec.yaml`中預設有如下配置:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211521877-482247186.png)
所有的圖示在**Icons**中已經定義,可以直接在原始碼中檢視,也可以到[官網檢視所有圖示](https://api.flutter.dev/flutter/material/Icons-class.html)。
所有圖示效果如下:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211522692-160515364.png)
### 案例
#### 聊天背景(.9圖實現)
```dart
Container(
width: 200,
padding: EdgeInsets.only(left: 8,top: 8,right: 20,bottom: 8),
decoration: BoxDecoration(
image: DecorationImage(
centerSlice: Rect.fromLTWH(20, 20, 1, 1),
image: AssetImage(
'assets/images/chat.png',
),
fit: BoxFit.fill)),
child: Text('老孟,專注分享Flutter技術和應用實戰。'
'老孟,專注分享Flutter技術和應用實戰。'
'老孟,專注分享Flutter技術和應用實戰。',),
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211522914-1197135581.webp)
背景圖片大小是57x80:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211523594-1782379367.webp)
右側三角已經不在中間了,如果想讓其一直保持居中,修改拉伸區域:
```dart
centerSlice: Rect.fromLTWH(20, 10, 1, 60),
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211524065-824102689.webp)
#### 圓形帶邊框的頭像
```dart
Container(
width: 100,
height: 100,
padding: EdgeInsets.all(3),
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.blue),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage('assets/images/aa.jpg'), fit: BoxFit.cover)),
),
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211524544-2134191314.webp)
#### 圖片佔位符:
```dart
Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/puffin.jpg',
height: 150,
width: 150,
fit: BoxFit.cover,
frameBuilder: (
BuildContext context,
Widget child,
int frame,
bool wasSynchronouslyLoaded,
) {
if (frame == null) {
return Image.asset(
'assets/images/place.png',
height: 150,
width: 150,
fit: BoxFit.cover,
);
}
return child;
},
)
```
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211524931-152928512.gif)
#### 新增自己的圖示庫
如果系統提供的圖示沒有我們想要的圖示,這時需要引入第三方庫的圖示,下面以阿里巴巴的圖示庫為例。
開啟[阿里巴巴的圖示官網]([https://www.iconfont.cn/),找到自己想要的圖示後,將滑鼠放置到圖示上,加入購物車:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211525858-334151127.png)
點選右上角的購物車,然後點選新增至專案:
如果沒有新增過專案,需要建立一個新專案:
建立好後加入此專案,跳轉到`我的專案`頁面,點選下載:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211526649-872264759.png)
解壓下載的檔案,解壓出來的檔案有好幾個,如下圖:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211526869-447824697.png)
選擇`iconfont.ttf`檔案拷貝到 Flutter 專案的`assets/fonts`目錄下,`assets/fonts`目錄預設是沒有的,需要手動建立,在`pubspec.yaml`設定如下:
千萬注意紅框內開頭的空格問題,否則編譯不通過,`family`後面跟的字串最好有意義,後面用圖示的時候需要用到。
用法如下:
```dart
Icon(IconData(0xe613,fontFamily: 'appIconFonts')
```
`0xe613`在下載圖示時已經標註,將`&#`替換為`0`,如下圖:
![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211527069-1803212367.png)
`fontFamily`是在`pubspec.yaml`中設定的`family`屬性,第三方的圖示和系統圖標一樣,可以設定其顏色和大小。
## 交流
老孟Flutter部落格地址(330個控制元件用法):[http://laomengit.com](http://laomengit.com)
歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:
| | |
| ----------------------------------------------- | ------------------------------------------------------------ |
| ![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211527755-1944497870.png) | ![](https://img2020.cnblogs.com/other/467322/202006/467322-20200616211528082-319589078