【Flutter 元件集錄】Flexible、Expanded 和 Spacer (上篇)

張風捷特烈發表於2021-08-19
前言:

這是我參與8月更文挑戰的第 19 天,活動詳情檢視:8月更文挑戰。為應掘金的八月更文挑戰,我準備在本月挑選 31 個以前沒有介紹過的元件,進行全面分析和屬性介紹。這些文章將來會作為 Flutter 元件集錄 的重要素材。希望可以堅持下去,你的支援將是我最大的動力~

本系列元件文章列表
1.NotificationListener2.Dismissible3.Switch
4.Scrollbar5.ClipPath6.CupertinoActivityIndicator
7.Opacity8.FadeTransition9. AnimatedOpacity
10. FadeInImage11. Offstage12. TickerMode
13. Visibility14. Padding15. AnimatedContainer
16.CircleAvatar17.PhysicalShape18.Divider
19.Flexible、Expanded 和 Spacer (上篇) [本文]

接下來的兩篇文章,將對 FlexibleExpandedSpacer 三個元件進行探討,包括使用及原始碼實現。

一、 認識 Flexible 元件

從原始碼中可以看出 Flexible 元件只能用於 RowColumnFlex 元件中。我們知道 RowColumn 的本質也是 Flex 元件,在很久之前寫過 Flex 的使用文章。今天來看一下 Flex 元件的御用周邊元件。Flexible的作用是:使子元件可以靈活地填充主軸的可用空間


1.Flexible 基本資訊

Flexible 繼承自 ParentDataWidget<FlexParentData> ,這個型別的父元件可能大家都沒見過,畢竟我們很少自定義 ParentDataWidget 型別的元件。Flexible 構造中必須傳入 child 元件。另外還有兩個引數:int 型的 flex,和 FlexFit 列舉型的 fit

可以說類的定義還是比較簡單的,下面一起看一下該元件的使用,及兩個屬性的作用。


2.Flexible 的使用

我們用如下的 Row 元件進行測試,外框是 Row 元件的區域,目前裡面只有一個頭像,佔位為深藍色區域。前面提到:Flexible可使子元件可以靈活地填充主軸中的可用空間。那麼這裡的可以空間就是 Row 除深藍色區域。

class FlexibleDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
        width: 200,
        height: 54,
        color: Colors.grey.withAlpha(11),
        child: Row(
            children:[
              buildHead(),
            ],
      ),
    );
  }

  Widget buildHead(){
    return Padding(
      padding: const EdgeInsets.all( 8.0),
      child: Image.asset('assets/images/icon_head.png',width: 40,height: 40,),
    );
  }
}
複製程式碼

現在做個小測試,在 Row 裡新增一個藍色的 Container ,那麼會如何顯示。Container 元件的佔位又會是怎樣呢?

Row(
    children:[
      buildHead(),
      Container(
        color: Colors.blueAccent,
      ),
    ]
)
複製程式碼

可以看出在為 54RowContainer 預設的約束為 [w(0,0) - h(0,54)] 。再加上 Container 在父區域內延展的特性,尺寸為 Size(0,54),也就是說它不可見。

當然,我們可以通過指定寬度來修改區域約束,讓 Container 顯示出來。但有個問題:如何讓 Container 填充剩餘的空間呢?

雖然我們可以通過計算剩餘尺寸來設定 Container的寬,但是這個計算過程比較麻煩,特別是 Row 裡子元件非常多或不固定,用算的自然比較費勁。其實這些 Flutter 內部已經幫你做了,並暴露一個 Flexible 元件來給你用。我們只需要簡單地套一個 Flexible 即可。

Row(
    children:[
      buildHead(),
      Flexible( //<--- 使用 Flexible
        child: Container(
          color: Colors.blueAccent,
        ),
      ),
    ],
  )
複製程式碼

可以看出加上 Flexible 元件後,約束變成了 [w(0,144) - h(0,54)] ,其中 144 是框架內部幫我們計算出來的。我們只需要使用 Flexible 元件,不必考慮計算的細節,是不是很妙。


3.Flexible 的 flex 屬性作用

如果只有一個子元件套 Flexible ,那麼 flex 屬性設成什麼都是一樣的效果。當有多個 Flexible 元件時,會根據 flex瓜分剩餘空間,如下是 藍 3 紅 1 的效果。

Row(
    children:[
      buildHead(),
      Flexible(
        flex:3,
        child: Container(
          color: Colors.blueAccent,
        ),
      ),
      Flexible(
        flex: 1,
        child: Container(
          color: Colors.red,
        ),
      )
    ],
),
複製程式碼

也就是藍色佔據剩餘寬度 (3/(3+1)) 的百分比,剩餘寬是 144 ,從樹中可以看出,第一個 Container 的寬確實是 108 。這樣多個 Flexible 元件時,根據 flex 屬性我們可以確定該元件在剩餘空間的佔比。


4.Flexible 的 fit 屬性作用

fit 是一個 FlexFit 型的列舉,只有兩個元素 tightloss ,所以並不是很難。預設下是 loss

enum FlexFit {
  tight,
  loose,
}
複製程式碼

tightloss 有什麼作用呢?我們通過之前的例子再看一下:如果 Flexible 包裹的子元件有固定的尺寸,預設情況下 loss 是無法使其區域延展的,甚至 Flexible 本身的尺寸也不會擴充套件。

Row(
    children:[
      buildHead(),
      Flexible(
        fit: FlexFit.loose,
        child: Container(
          width: 40,
          color: Colors.blueAccent,
        ),
      ),
    ],
),
複製程式碼

當為 tight 時,Flexible會強制 延展。這就是兩者最大的區別。

Row(
    children:[
      buildHead(),
      Flexible(
        fit: FlexFit.tight,
        child: Container(
          width: 40,
          color: Colors.blueAccent,
        ),
      ),
    ],
),
複製程式碼

可能 Container 自身的延展性上面的例子體現並不明顯。可以通過一個固定尺寸的 Icon 來說明一下:如下左側是 loose ,右側是 tight 。只要記住 tight 會強制延展自身區域即可。


二、Expanded 元件的實現

Expanded 的實現非常非常簡單,下面是它的全部程式碼。它就是繼承自 Flexible 元件,將 fit 值固定為 tight 而已,所以說 Expanded 就是一個強制延展的 Flexible 元件。

兩者在使用上並沒有什麼區別,由於 Flexible 可以設定 fit 值,所以用途要比 Expanded 廣泛。而強制延展的場景使用 Expanded 元件語義更好,而且簡單一點。


三、Spacer 元件的實現

Expanded 的實現也非常簡單,下面是它的全部程式碼。它是一個 StatelessWidget ,內部依賴 Expanded 元件實現功能,特點是:它不能設定子元件,本身作為空白佔位使用。

總的來說 SpacerExpanded 的功能都基於 Flexible 元件實現。瞭解了 Flexible 元件的使用,就可以一通百通。那本文到這裡就結束了,謝謝觀看,明天見~

相關文章