【Flutter 元件集錄】SizedBox | 8 月更文挑戰

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

這是我參與8月更文挑戰的第 21 天,活動詳情檢視: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 20.Card21.SizedBox [本文]

一、認識 SizedBox 元件

原始碼中對 SizedBox 的介紹為:一個指定尺寸的盒子。那 SizedBox 為什麼可以限定尺寸?背後區域限定的原理又是什麼? 本文通過 SizedBox 來一窺佈局約束奧祕的冰山一角。


1.SizedBox 基本資訊

下面是 SizedBox 元件類的定義構造方法,可以看出它繼承自 SingleChildRenderObjectWidget。可接受一個子元件,和區域的寬高。


2.SizedBox 的使用

如下,是一個 100*50SizedBox ,通過 ColoredBox 塗上藍色,效果如下:

SizedBox(
  width: 100,
  height: 50,
  child: ColoredBox(
    color: Colors.blue.withAlpha(88)
  ),
),
複製程式碼

3.區域分析

乍一看,不就是一個元件提供寬高來設定尺寸嗎,似乎並沒有什麼好延伸的。但你有沒有想過,為什麼 SizedBox 有權力決定尺寸大小?它決定的區域一定有效嗎?在分析之前,先了解一些前置知識:

任何元件的佔位區域繪製內容最終都取決於 RenderObject 。而並非所有的元件都和 RenderObject 有關,只有 RenderObjectWidget 負責維護 RenderObject 。像 StatelessWidgetStatefulWidget 這種都是基於已有元件進行組合,往深層去看,他們都是基於某些 RenderObjectWidget 實現。

關於佈局, RenderObject 有一個非常重要的屬性: Constraints 型別的 constraints ,表示自身受到的區域約束限制。而 RenderBox 作為 RenderObject 的子類,擴充出了 size 的概念,絕大多陣列件維護的渲染物件都是在 RenderBox 基礎上進行擴充的。

下面來開啟元件樹,一起來看一下:

上面的 SizedBox 元件,它維護的 RenderObjectRenderConstrainedBox ,自身的約束為 [w(0,800) - h(0,600)] ,也就說明該渲染物件的大小必須在這此區間內。然後它會給子元件施加一個額外的約束 [w(100,100) - h(50,50)]

這樣對於 ColoredBox 對應的渲染物件 _RenderColoredBox ,由於父級施加的額外約束,自身的約束也就變成 [w(100,100) - h(50,50)] 。也就說明該渲染物件的大小必須在這此區間內,即 _RenderColoredBox 的尺寸被限定為 (100,50)

_RenderColoredBoxsize 確定後,RenderConstrainedBox 會根據自身的約束和子節點的尺寸來確定自身的尺寸。這就是 SizedBox 的工作原理。


4、約束測試

為了更好地說明約束的作用,這裡進行一下測試,在之前的案例的 SizedBox 外層通過 ConstrainedBox 元件新增新增一個 [w(20,20) - h(20,20)] 的強制約束。可以看出即使 SizedBox 設定了固定的寬高,但是在外層的約束之下,會優先滿足父級約束。

[推論1] SizedBox 的最終尺寸會受到父級約束的影響,並非一定為指定值。
複製程式碼

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: 20,
    maxWidth: 20,
    maxHeight: 20,
    minHeight: 20,
  ),
  child: SizedBox(
    width: 100,
    height: 50,
    child: ColoredBox(color: Colors.blue.withAlpha(88)),
  ),
);
複製程式碼

我們再來看一下此時的元件樹:
可以看出 SizedBox 維護的 RenderConstrainedBox 本身的約束區域為 [w(20,20) - h(20,20)] ,為子節點施加的額外約束為 [w(100,100) - h(50,50)] 。在 ColoredBox 維護的 _RenderColoredBox 中,約束區域為 [w(20,20) - h(20,20)] ,這也就覺得了其尺寸為 (20,20)

這樣可以看出,渲染物件對子節點施加的額外約束 ,並不會完全作用於子節點。還會根據自身的約束情況,來確定子元件的最終約束。


三、SizedBox 的原始碼分析

SizedBox 繼承自 SingleChildRenderObjectWidget ,就說明它需要維護一個 RenderObject 來實現功能。


在前面我們通過元件樹可以看出,它維護的渲染物件是 RenderConstrainedBox 。從原始碼中可以看出, RenderConstrainedBox 構造時需要傳入一個約束物件 BoxConstraints 。這裡通過 BoxConstraints.tightFor 構造使用 widthheight 建立一個緊約束。

通過原始碼可以看出,這個構造的約束為: [w(width,width) - h(height,height)],也就是固定寬高約束。


SizedBox 除了普通構造之外,還有三個命名構造。如果已經瞭解上面的用法,那這三個也非常簡單,都逃離不了對寬高的初始化。比如 .expand 會建立一個無限的約束,這樣由於 推論1 ,其約束的尺寸就可以在父級的約束下,儘可能的大 。 .shrink 就是一個 [w(0,0) - h(0,0)]的限制,同理,會在父級的約束下,儘可能的小。

至於 RenderConstrainedBox 渲染物件的實現,將在後面的 ConstrainedBox 一文中進行介紹,畢竟 RenderConstrainedBox 的本命是 ConstrainedBox 。通過本文,你應該對 SizedBox 有了更深的認識,對佈局約束、尺寸確定也認識了九牛一毛 。那本文到這裡就結束了,謝謝觀看,明天見~

相關文章