Flutter元件學習(二)—— Image

24K純帥豆發表於2019-03-04

序言

上一節中,我們講了 FlutterText 元件的一些用法以及 API,本節我們繼續學習 Flutter 中的 Image 元件,同樣先上圖:

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 的圖片風格,分為 1x2x3x,具體做法是在專案的根目錄下建立兩個資料夾,如下圖所示:

Flutter元件學習(二)—— Image

然後需要在 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,
)
複製程式碼

第一種方法是載入一個本地的佔點陣圖,第二種是載入一個透明的佔點陣圖,但是需要注意的是,這個元件是不可以設定載入出錯顯示的圖片的;這裡有另一種方法可以使用第三方 packageCachedNetworkImage 元件:

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等技術;在這裡你能得到的不止是技術上的提升,還有一些學習經驗以及志同道合的朋友,趕快加入我們,一起學習,一起進化吧!!!

公眾號:IT先森養成記

相關文章