天天看點

文本、圖檔和按鈕在Flutter中怎麼用

與iOS、Android和React類似,作為一個UI架構,Flutter自然也提供了很多UI控件。而文本、圖檔和按鈕,則是這些不同的UI架構中建構視圖都要用到的三個最基本的控件。

Flutter中的文本Text和圖檔Image,我在前面的文章中都有過介紹,今天我們再來詳細地聊一聊。

文本控件

Flutter中,Text支援兩種類型的文本展示,一個是預設的展示單一樣式的文本 Text,另一個是支援多種混合樣式的富文本 Text.rich。

我們先來看看如何使用單一樣式的文本 Text。

單一樣式文本Text的初始化,是需要傳入要展示的字元串。而這個字元串的具體展示效果,受構造函數中的其他參數控制。這些參數大緻可以分為兩類:

  • 控制整體文本布局的參數,如文本對齊方式 textAlign、文本排版方向 textDirection、文本顯示最大行數 maxLines、文本截斷規則 overFlow等,這些都是構造函數中的參數。
  • 控制文本展示樣式的參數,如字型名稱 fontFamily、字型大小 fontSize、文本顔色 color、文本陰影 shadows 等等,這些參數被統一封裝到了構造函數中的參數 style中。

接下來,我們以一個具體的例子來看看 Text控件的使用方法。如下所示,我在代碼中定義了一段居中布局、20号紅色粗體展示樣式的字元串:

Text(
        "這是一段居中布局、20号紅色粗體展示樣式的文本",
        textAlign: TextAlign.center,//居中展示
        style: TextStyle(
          fontSize: 20,
          fontWeight: FontWeight.bold,
          color: Colors.red
        ),//20号紅色粗體
      );           

複制

運作效果如下圖所示:

文本、圖檔和按鈕在Flutter中怎麼用

了解了單一樣式文本Text的使用方法後,我們再來看看如何在一段字元串中支援多種混合展示樣式。

混合展示樣式與單一展示樣式的關鍵差別在于分片,即如何把一段字元串分為幾個片段來管理,給每個片段單獨設定樣式。面對這樣的需求,在Android中,我們使用 SpannableString來實作;在iOS中,我們使用NSAttributedString來實作;而在Flutter中國也有類似的概念,即TextSpan。

TextSpan定義了一個字元串片段該如何控制其展示樣式,而将這些有着獨立展示樣式的字元串組裝在一起,則可以支援混合樣式的富文本展示。

如下方代碼所示,我們分别定義了黑色與紅色兩種展示樣式,随後把一段字元串分成了4個片段,并設定了不同的展示樣式:

Text.rich(
        TextSpan(
          children: <TextSpan>[
            TextSpan(text: "文本是視圖系統中常見的控件,它用來顯示一段特定樣式的字元串,類似", style: redStyle),
            TextSpan(text: "iOS", style: blackStyle),
            TextSpan(text: "中的", style: redStyle),
            TextSpan(text: "UILabel", style: blackStyle)
          ],
        ),
        textAlign: TextAlign.center,
      );           

複制

運作效果,如下圖所示:

文本、圖檔和按鈕在Flutter中怎麼用

接下來,我們再來看看Flutter中的圖檔控件Image。

圖檔

使用Image,可以讓我們向使用者展示一張圖檔。圖檔的顯示方式有很多,比如資源圖檔、網絡圖檔、檔案圖檔等,圖檔格式也各不相同,是以在Flutter中,也有多種方式用來加載不同形式、支援不同格式的圖檔:

  • 加載本地資源圖檔,如:

    Image.asset("images/001.jpg")

  • 加載網絡圖檔,如:

    Image.network("http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg")

除了可以根據圖檔的顯示方式設定不同的圖檔源之外,圖檔的構造方法還提供了填充模式fit、拉伸模式centerSlice、重複模式repeat等屬性,可以針對圖檔與目标區域的寬高比差異制定排版模式。這,和Android中的ImageView、iOS中的UIImageView的屬性都是類似的,我在Flutter的圖檔元件這篇文章中有做詳細介紹。

關于圖檔展示,我想和你着重分享一下Flutter中的FadeInImage控件。在加載網絡圖檔的時候,為了提升使用者的等待體驗,我們往往會加入占位圖、加載動畫等元素,但是預設的Image.network方法并不支援這些進階功能,這個時候,FadeInImage控件就派上用場了。

FadeInImage控件提供了圖檔占位的功能,并且支援在圖檔加載完成時淡入淡出的視覺效果。此外,由于Image支援gif格式,我們甚至還可以将一些炫酷的加載動畫作為占位圖。

下述代碼展示了這樣的場景。我們在加載大圖檔時,将一張 loading 的 gif 作為占位圖展示給使用者:

FadeInImage.assetNetwork(
          image: "http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg",
          placeholder: "images/002.gif",//gif 占位
          width: 200,
          height: 200,
          fit: BoxFit.cover,
        );           

複制

Image控件需要根據圖檔資源異步加載的情況,決定自身的顯示效果,是以是一個StatefulWidget。圖檔加載過程由 ImageProvider 觸發,而 ImageProvider 表示異步擷取圖檔資料的操作,可以從資源檔案、網絡等不同的管道擷取圖檔。

首先, ImageProvider 根據 _ImageState 中傳遞的圖檔配置,生成對應的圖檔緩存key,然後去ImageCache中查找是否有對應的圖檔緩存,如果有,則通知 _ImageState 重新整理UI;如果沒有,則啟動ImageStream開始異步加載,加載完畢後,更新緩存;最後,通知 _ImageState 重新整理UI。

Image展示圖檔的流程,可以用如下流程圖來表示:

文本、圖檔和按鈕在Flutter中怎麼用

值得注意的是,ImageCache使用LRU(Least Recently Used,即最近最少使用)算法來進行緩存更新政策,并且預設最多存儲1000張圖檔。最大緩存限制為100MB,當限定的空間已經存滿資料時,把最久沒有被通路到的圖檔清除。圖檔緩存隻會在運作期間生效,也就是隻緩存在記憶體中。如果想要支援緩存到檔案系統,可以使用第三方的 CachedNetworkImage 控件(https://pub.dev/packages/cached_network_image)。

CachedNetworkImage的使用方法與 Image 類似,除了支援圖檔緩存之外,它還提供了比FadeInImage更為強大的加載過程占位與加載錯誤占位。

在下面的代碼中,我們在加載圖檔時,不僅給使用者展示了作為占位的轉圈loading,還提供了一個錯誤圖兜底,以備圖檔加載出錯:

CachedNetworkImage(
        imageUrl: "http://pic27.nipic.com/20130315/11511914_151013608193_28.jpg",
        placeholder: (context, url) => new CircularProgressIndicator(),
        errorWidget: (context, url, error) => new Icon(Icons.error),
     )           

複制

最後,我們再來看看Flutter中的按鈕控件。

按鈕

通過按鈕,我們可以響應使用者的互動事件。Flutter提供了三個基本的按鈕控件:FloatingActionButton、FlatButton和RaisedButton。

  • FloatingActionButton:一個圓形的按鈕,一般出現在螢幕内容的前面,用來處理界面中最常用、最基礎的使用者動作。
  • RaisedButton:凸起的按鈕,預設帶有灰色背景,被點選後灰色背景會加深。
  • FlatButton:扁平化的按鈕,預設透明背景,被點選後會呈現灰色背景。

這三個按鈕控件的使用方法類似,唯一的差別隻是預設樣式不同而已。

下面代碼中,我分别定義了FloatingActionButton、FlatButton和RaisedButton,它們的功能完全一樣,在點選時列印一段文字:

FloatingActionButton(onPressed: () => print('FloatingActionButton pressed'),child: Text('Btn'),);
FlatButton(onPressed: () => print('FlatButton pressed'),child: Text('Btn'),);
RaisedButton(onPressed: () => print('RaisedButton pressed'),child: Text('Btn'),);           

複制

文本、圖檔和按鈕在Flutter中怎麼用

既然是按鈕,是以除了控制基本樣式之外,還需要響應使用者點選行為。這就對應着按鈕控件中兩個最重要的參數了:

  • onPressed 參數用于設定點選回調,告訴Flutter在按鈕點選時通知我們。如果 onPressed 參數為空,則按鈕會處于禁用狀态,不響應使用者點選。
  • child 參數用于設定按鈕的内容,告訴Flutter控件應該長成什麼樣,也就是控制着按鈕控件的基本樣式。child 可以接收任意的Widget,比如Text、Image等控件。

雖然我們可以通過 child 參數來控制按鈕控件的基本樣式,但是系統預設的樣式還是太單調了。是以通常情況下,我們還是會進行控件樣式定制。

與Text控件類似,按鈕控件也提供了豐富的樣式定制功能,比如背景顔色color、按鈕形狀shape、主題顔色colorBrightness,等等。

接下來,我就以FlatButton為例,與你介紹按鈕的樣式定制:

FlatButton(
        child: Row( children: <Widget>[Icon(Icons.add), Text("Add")], ),
        onPressed: ()=>print("FlatButton Pressed"),
        color: Colors.yellow,//設定背景顔色為黃色
        shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(20)),//設定斜角矩形邊框
        colorBrightness: Brightness.light,//確定文字按鈕為深色
      );           

複制

可以看到,我們将一個加号Icon與文本組合,定義了按鈕的基本外觀;随後通過 shape 來指定其外形為一個斜角矩形邊框,并将按鈕的背景色設定為黃色。

因為按鈕背景顔色是淺色的,為避免按鈕文字看不清楚,我們通過設定按鈕主題 colorBrightness 為 Brightness.light ,保證按鈕文字顔色為深色。

展示效果如下:

文本、圖檔和按鈕在Flutter中怎麼用

總結

UI控件是建構一個視圖的基本元素,而文本、圖檔和按鈕則是其中最經典的控件。

接下來,我們簡單回顧一下今天的内容,以便加深了解與記憶。

首先,我們認識了支援單一樣式與混合樣式兩種類型的文本展示控件Text。其中,通過TextStyle控制字元串的展示樣式,其他參數控制文本布局,可以實作單一樣式的文本展示;而通過TextSpan将字元串分割為若幹片段,對每個片段單獨設定樣式後組裝,可以實作支援混合樣式的富文本展示。

然後,我們學習了支援多種圖檔源加載方式的圖檔控件Image。Image内部通過ImageProvider根據緩存狀态,觸發異步加載流程,通知_ImageState重新整理UI。不過,由于圖檔緩存是記憶體緩存,是以隻在運作期間生效。如果要支援緩存到檔案系統,可以使用第三方的CacheNetworkImage。

最後,我們學習了按鈕控件,Flutter提供了多種按鈕控件,而它們的使用方法也都類似。其中,控件初始化的child參數用于設定按鈕長什麼樣,而onPressed參數則用于設定點選回調。與Text類似,按鈕内部也有豐富的UI定制接口,可以滿足開發者的需求。

其實,在UI基本資訊的表達上,Flutter的經典控件與原生iOS、Android系統提供的控件沒有什麼本質差別。但是在自定義控件樣式上,Flutter的這些經典控件提供了強大而簡介的擴充能力,使得我們可以快速開發出功能複雜、樣式豐富的頁面。

以上。