影象元件。
-
通過資源嵌入(Image.asset) 可載入本地資源圖片
-
通過檔案嵌入(Image.file) 可載入本地圖片檔案
-
通過網路嵌入(Image.network) 可載入網路圖片
-
通過記憶體嵌入(Image.memory) 可載入Uint8List(Byte 陣列)資源
-
通過盒適配(BoxFit) 可分別設定影象的適配配型
- BoxFit.contain
- BoxFit.cover
- BoxFit.fill
- BoxFit.none
- BoxFit.scaleDown
-
通過(BlendMode) 可設定圖片的顏色混合選項
1、插入本地資源圖片
首次插入本地圖片前,需要修改 本地專案根路徑 中的檔案 "pubspec.yaml"
找到包含下列程式碼的行:
flutter:
複製程式碼
新增下列行:
assets:
- assets/images/
複製程式碼
操作完畢後,你的檔案應該看起來像這樣:
# …
flutter:
assets:
- assets/images/
# …
複製程式碼
隨後,你應在專案根路徑中(與 "pubspec.yaml" 同級) 建立下列目錄: assets/images/
即可使用下列程式碼引入存在於專案中的圖片
new Image.asset(
"assets/images/thumbnail.png"
)
複製程式碼
上述程式碼等效於:
<img src="assets/images/thumbnail.png" />
複製程式碼
有關於 "pubspec.yaml" 檔案修改的詳細資訊,請參照 flutter 中文網中相關章節的描述。
指定 assets flutterchina.club/assets-and-…
2、從檔案系統中插入圖片
[BECARE!] 本示例[2、從檔案系統中插入圖片]程式碼以安卓平臺為執行環境,不能確保其他平臺的穩健及可執行性。
此方法依賴於 path_provider
因此,需要在 "pubspec.yaml" 中新增並安裝此依賴。
開啟 "pubspec.yaml",
在 dependencies: 下新增如下依賴
path_provider: ^1.3.0
複製程式碼
最新版本的版本號可從下列地址中獲得:
新增依賴後,你的 "pubspec.yaml" 檔案應該看起來像這樣:
# …
dependencies:
flutter:
sdk: flutter
# …
path_provider: ^1.3.0
# …
複製程式碼
隨後,我們執行下列指令碼安裝所需依賴。
flutter pub get
下列程式實現了從檔案系統中讀取圖片。
** 一開始,檔案系統中不存在這張圖,因此我們需要在裝置中手動儲存一張圖片。 **
這裡使用 getExternalStorageDirectory()進行實現,其在Android系統中的位置為:
/storage/emulated/0/Android/data/應用包名/files/
請在其中新建名為 thumbnail.png
的圖片
下列程式碼實現非同步讀取外部儲存目錄位置,並通過 Image.File 讀取其根路徑下的圖片。
import "package:flutter/material.dart";
// getExternalStorageDirectory 方法依賴於下列 package
import 'package:path_provider/path_provider.dart';
// File 元件依賴於下列 package
import "dart:io";
class Image$FileState extends State<Image$File> {
//定義 _pathName state, 用於儲存非同步讀取到的系統外部儲存路徑
String _pathName;
//定義初始狀態
@override
void initState() {
super.initState();
//呼叫 _getLocalFile(), 非同步讀取系統外部儲存路徑
_getLocalFile().then((value) {
//將其寫入 _pathName 中
setState(() {
_pathName = value;
});
});
}
Future<String> _getLocalFile() async {
// 獲取外部儲存路徑,暫存於 dir 中
String dir = (await getExternalStorageDirectory()).path;
//返回外部儲存路徑下名為 thumbnail.png 的檔案
return "$dir/thumbnail.png";
}
//建立狀態示例
@override
Image$FileState createState() => Image$FileState();
//進行元件具體實現
Widget build(BuildContext context){
//由於 _pathName 是非同步獲得,所以在 _pathName 未取到時,需要返回 null
return null != _pathName ? Image.file(
File(_pathName)
): null;
}
}
class Image$File extends StatefulWidget {
@override
Image$FileState createState() => Image$FileState();
}
複製程式碼
上述示例等效於:
<img src="file:///storage/emulated/0/Android/data/com.demo.flutterapp/files/thumbnail.png" />
複製程式碼
注: 此方法可能需要系統許可權。
安卓可以修改 AndroidManifest.xml
增加下列程式碼即可:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
複製程式碼
3、從網路中插入圖片
new Image.network(
"https://www.baidu.com/img/bd_logo1.png"
)
複製程式碼
等效於:
<img src="https://www.baidu.com/img/bd_logo1.png" />
複製程式碼
4、從 Uint8List 中獲取圖片
本示例中, 我們使用 base64Decode 方法將 base64 字串轉換為 UInt8List,
之後, 利用 Image.memory() 從記憶體中讀取圖片。
import "package:flutter/material.dart";
//base64Decode 方法依賴於下列 package
import "dart:convert";
class Image$Memory extends StatelessWidget {
//使用 base64Decode方法將base64字串解碼為 UInt8List
final List _base64 = base64Decode("R0lGODlhdQAmAKIAAOYyL+rU4llg6Jmd8e92dCky4eEGAv///yH5BAAAAAAALAAAAAB1ACYAAAP/eLrc/jC2IEoZMATJu/9gyFVWIUyksIls677LUJbrEcxWDe98f+CWk4I0w/iOSNANKJQBC8mo9LEEDp8F3XR7rOIU2Cx3jHwKsUKyWqRhEEvGN3xN91BoCq8l9tTW/244Rk4mOkBGgIl8VjF+d4V5A5KKf3IWiCCEOZRraGxPnGqeIZpzoVyjDBptDpYmp1yumI9BWq5QUQS6fn+lm3lYmLdSBsW8xcjJBgAABLwfAMhXRQt6ODWuJ8rb3AYK0d3h4OHIBC7jC0TCYb/ZB+Th3/Dc4/PmLOgSvkwK+xjMAAEmCwhQnrc8rFSxGkiwnoFnEnTp0mdqlJw0DgIggwgu/08xABM+KkhGJdm9JJqMhGkyQIAATA3HxVwATuO8jyQfECiXJyYzZwzyOSilcqWEmzkP1ES6LGmDcStshjspNJAgMOwwNmBajGZXGyJVhU22quxOaR7hrahqQ52DC1jh/nj1AG0Eux3BLgu59x3SkxHO9mVLhJfcirUc2IWA96vUbvf+QkNbldCgIGCKHi6h1e/Bu1+VOiYHYAVTwBHyCfWyocoZzZovbf3ok1njzx/IltUg+GG13twMfta0gdDruLH3MOD6uaPDcI8hCAZp4/k24elmtOacGXlcPAuYe/VmnVt0ncioTyeYU+ibQRWOHyCRXEz40BDyivYWYBzQAIbTbeBUNTIZRF1QlNlVimGdfffLSPg9oJ9+vVV4H23smWSgFqrZZc0AWiTWACMXcjAhfgAqgxpXKwDXDXbZsSPjVcth6NNtZj3nzCpM1dDfPDACM+OQ9l2I1HiPlfNceentMoGLygQp5Eq3aKfYaUgOdBIB9RyYSEsmvASiFgG0RMRLEK2RAAA7");
//進行元件具體實現
Widget build(BuildContext context){
return new Image.memory(
_base64
);
}
}
複製程式碼
上述示例等效於:
<img src="data:image/gif;base64,R0lGODlhdQAmAKIAAOYyL+rU4llg6Jmd8e92dCky4eEGAv///yH5BAAAAAAALAAAAAB1ACYAAAP/eLrc/jC2IEoZMATJu/9gyFVWIUyksIls677LUJbrEcxWDe98f+CWk4I0w/iOSNANKJQBC8mo9LEEDp8F3XR7rOIU2Cx3jHwKsUKyWqRhEEvGN3xN91BoCq8l9tTW/244Rk4mOkBGgIl8VjF+d4V5A5KKf3IWiCCEOZRraGxPnGqeIZpzoVyjDBptDpYmp1yumI9BWq5QUQS6fn+lm3lYmLdSBsW8xcjJBgAABLwfAMhXRQt6ODWuJ8rb3AYK0d3h4OHIBC7jC0TCYb/ZB+Th3/Dc4/PmLOgSvkwK+xjMAAEmCwhQnrc8rFSxGkiwnoFnEnTp0mdqlJw0DgIggwgu/08xABM+KkhGJdm9JJqMhGkyQIAATA3HxVwATuO8jyQfECiXJyYzZwzyOSilcqWEmzkP1ES6LGmDcStshjspNJAgMOwwNmBajGZXGyJVhU22quxOaR7hrahqQ52DC1jh/nj1AG0Eux3BLgu59x3SkxHO9mVLhJfcirUc2IWA96vUbvf+QkNbldCgIGCKHi6h1e/Bu1+VOiYHYAVTwBHyCfWyocoZzZovbf3ok1njzx/IltUg+GG13twMfta0gdDruLH3MOD6uaPDcI8hCAZp4/k24elmtOacGXlcPAuYe/VmnVt0ncioTyeYU+ibQRWOHyCRXEz40BDyivYWYBzQAIbTbeBUNTIZRF1QlNlVimGdfffLSPg9oJ9+vVV4H23smWSgFqrZZc0AWiTWACMXcjAhfgAqgxpXKwDXDXbZsSPjVcth6NNtZj3nzCpM1dDfPDACM+OQ9l2I1HiPlfNceentMoGLygQp5Eq3aKfYaUgOdBIB9RyYSEsmvASiFgG0RMRLEK2RAAA7"
/>
複製程式碼
5、圖片的適配
在瞭解此示例之前,請拷貝下列模板程式碼至專案中。
替換其 Image 元件的內容以檢視效果:
import "package:flutter/material.dart";
import "dart:convert";
class Image$Fit extends StatelessWidget {
//進行元件具體實現
Widget build(BuildContext context){
return new Scaffold(
body: new Center(
child: new Container(
color: Colors.blue,
child: new Image.network(
"https://www.baidu.com/img/bd_logo1.png"
),
constraints: BoxConstraints.expand(
width: 300,
height: 200
)
)
)
);
}
}
複製程式碼
這時,可以看到頁面中央有一個藍色容器,裡面伴隨一張圖片。
我們替換此 new Image
中的內容,以瀏覽不同的圖片適配效果。
5.1、contain
在 contain 模式中, 被替換的內容將被縮放, 以在填充元素的內容框時保持其寬高比。
整個物件在填充盒子的同時保留其長寬比,因此如果寬高比與框的寬高比不匹配,該物件將被新增 "白邊"。
new Image.network(
"https://www.baidu.com/img/bd_logo1.png",
fit: BoxFit.contain
)
複製程式碼
等效於:
<!--
此 #demo-wrapper 僅為方便檢視效果而設,
後續有關 object-fit 的示例均將在此容器內表現 ?
-->
<div
id="demo-wrapper"
style=
"
width: 300px;
height: 200px;
background: blue;
display: flex;
flex-wrap: nowrap;
align-items: flex-start;
"
>
<!--
此 #demo-wrapper 僅為方便檢視效果而設,
後續有關 object-fit 的示例均將在此容器內表現 ?
-->
<img
src="https://www.baidu.com/img/bd_logo1.png"
style=
"
width: 100%;
height: 100%;
obsect-fit: contain;
"
/>
</div>
複製程式碼
5.2、contain
在 cover 模式中, 被替換的內容在保持其寬高比的同時填充元素的整個內容框。
如果物件的寬高比與內容框不相匹配, 該物件將被剪裁以適應內容框。
new Image.network(
"https://www.baidu.com/img/bd_logo1.png",
fit: BoxFit.cover
)
複製程式碼
等效於:
<img
src="https://www.baidu.com/img/bd_logo1.png"
style=
"
width: 100%;
height: 100%;
obsect-fit: cover;
"
/>
複製程式碼
5.3、fill
在 fill 模式中, 被替換的內容正好填充元素的內容框。
整個物件將完全填充此框, 如果物件的寬高比與內容框不相匹配, 那麼該物件將被拉伸以適應內容框。
new Image.network(
"https://www.baidu.com/img/bd_logo1.png",
fit: BoxFit.fill
)
複製程式碼
等效於:
<img
src="https://www.baidu.com/img/bd_logo1.png"
style=
"
width: 100%;
height: 100%;
object-fit: fill;
"
/>
複製程式碼
5.4、none
在 none 模式中, 被替換的內容將保持其原有的尺寸。
new Image.network(
"https://www.baidu.com/img/bd_logo1.png",
fit: BoxFit.none
)
複製程式碼
等效於:
<img
src="https://www.baidu.com/img/bd_logo1.png"
style=
"
width: 100%;
height: 100%;
object-fit: none;
"
/>
複製程式碼
5.5、scale-down
在 scale-down 模式中, 內容的尺寸與 none 或 contain 中的一個相同, 取決於它們兩個之間誰得到的物件尺寸會更小一些。
new Image.network(
"https://www.baidu.com/img/bd_logo1.png",
fit: BoxFit.scaleDown
)
複製程式碼
等效於:
<img
src="https://www.baidu.com/img/bd_logo1.png"
style=
"
width: 100%;
height: 100%;
object-fit: scale-down;
"
/>
複製程式碼
更多有關 object-fit 的資料, 請查閱下面的連結:
5.6、其他適配模式
除上述適配模式外,
flutter 支援 Boxfit.fitWidth
/ Boxfit.fitHeight
等獨佔的適配模式,
這些獨佔的適配模式沒有對應的 web 程式碼實現,請自行了解。
參考資料:
Flutter 圖片如何充滿父佈局 www.jianshu.com/p/8810bacfe…
6、混合模式
new Image.network(
"https://www.baidu.com/img/bd_logo1.png",
colorBlendMode: BlendMode.color,
color: Color(0xFF0000FF)
)
複製程式碼
等效於下列 web 程式碼:
<div
style=
"
display: inline-block;
position: relative;
font-size: 0;
"
>
<div
style=
"
mix-blend-mode: color;
width: 100%;
height: 100%;
position: absolute;
background-color: #0000FFFF;
"
>
</div>
<img src="https://www.baidu.com/img/bd_logo1.png" />
</div>
複製程式碼
其中,Flutter 與 web 等效的表格對映如下:
混合模式 | web 實現 | flutter 實現 | 說明 |
---|---|---|---|
正常 | normal | 未實現 | 混合色的畫素會透過所用的顏色顯示出來 |
正片疊底 | multiply | BlendMode.multiply | 在 「正片疊底」 模式中, 檢視每個通道中的顏色資訊, 並將 「基色」 與「混合色」複合。 |
濾色 | screen | BlendMode.screen | 「濾色」模式與「正片疊底」模式正好相反, 它將影象的「基色」顏色與「混合色」顏色結合起來產生比兩種顏色都淺的第三種顏色 |
疊加 | overlay | BlendMode.overlay | 「疊加」模式把影象的「基色」顏色與「混合色」顏色相混合產生一種中間色。 |
變暗 | darken | BlendMode.darken | 在「變暗」模式中, 檢視每個通道中的顏色資訊, 並選擇「基色」或「混合色」中較暗的顏色作為「結果色」。 |
變亮 | lighten | BlendMode.lighten | 在「變亮」模式中, 檢視每個通道中的顏色資訊, 並選擇「基色」或「混合色」中較亮的顏色作為「結果色」。 |
顏色減淡 | color-dodge | BlendMode.colorDodge | 在「顏色減淡」模式中, 檢視每個通道中的顏色資訊, 並通過減小對比度使基色變亮以反映混合色。與黑色混合則不發生變化。 |
顏色加深 | color-burn | BlendMode.colorBurn | 在「顏色加深」模式中, 檢視每個通道中的顏色資訊, 並通過增加對比度使基色變暗以反映混合色, 如果與白色混合的話將不會產生變化。 |
強光 | hard-light | BlendMode.hardLight | 「強光」模式將產生一種強光照射的效果。如果「混合色」顏色比「基色」顏色的畫素更亮一些, 那麼「結果色」顏色將更亮;如果「混合色」顏色比「基色」顏色的畫素更暗一些, 那麼「結果色」將更暗。 |
柔光 | soft-light | BlendMode.softLight | 「柔光」模式會產生一種柔光照射的效果。如果「混合色」顏色比「基色」顏色的畫素更亮一些, 那麼「結果色」將更亮; 如果「混合色」顏色比「基色」顏色的畫素更暗一些, 那麼「結果色」顏色將更暗, 使影象的亮度反差增大。 |
差值 | difference | BlendMode.difference | 在「差值」模式中, 檢視每個通道中的顏色資訊, 「差值」模式是將從影象中「基色」顏色的亮度值減去「混合色」顏色的亮度值, 如果結果為負, 則取正值, 產生反相效果。 |
排除 | exclusion | BlendMode.exclusion | 「排除」模式與「差值」模式相似, 但是具有高對比度和低飽和度的特點。比用「差值」模式獲得的顏色要柔和、更明亮一些。 |
色相 | hue | BlendMode.hue | 「色相」模式只用「混合色」顏色的色相值進行著色, 而使飽和度和亮度值保持不變。 |
飽和度 | saturation | BlendMode.saturation | 「飽和度」模式的作用方式與「色相」模式相似, 它只用「混合色」顏色的飽和度值進行著色, 而使色相值和亮度值保持不變。 |
顏色 | color | BlendMode.color | 「顏色」模式能夠使用「混合色」顏色的飽和度值和色相值同時進行著色, 而使「基色」顏色的亮度值保持不變。「顏色」模式模式可以看成是「飽和度」模式和「色相」模式的綜合效果。 |
亮度 | luminosity | BlendMode.luminosity | 「亮度」模式能夠使用「混合色」顏色的亮度值進行著色, 而保持「基色」顏色的飽和度和色相數值不變。其實就是用「基色」中的「色相」和「飽和度」以及「混合色」的亮度建立「結果色」。 |
此外, Flutter 支援 BlendMode.plus
/ BlendMode.dst
/ BlendMode.src
等其他獨佔混合模式。
這些獨佔的混合模式沒有對應的 web 程式碼實現,請自行了解。
參考資料:
BlendMode(影象混合模式) www.jianshu.com/p/4fb8f1a08…
參考文獻
[1] Flutter基礎視訊教程 技術胖 2018年11月 P10 09.Image元件的使用 www.bilibili.com/video/av358…
[2] css mix-blend-mode 混合模式 blog.csdn.net/Geoooo/arti…