Flutter 佈局(五)- LimitedBox、Offstage、OverflowBox、SizedBox詳解

吹個大氣球發表於2018-07-21

本文主要介紹Flutter佈局中的LimitedBox、Offstage、OverflowBox、SizedBox四種控制元件,詳細介紹了其佈局行為以及使用場景,並對原始碼進行了分析。

1. LimitedBox

A box that limits its size only when it's unconstrained.

1.1 簡介

LimitedBox,通過字面意思,也可以猜測出這個控制元件的作用,是限制型別的控制元件。這種型別的控制元件前面也介紹了不少了,這個是對最大寬高進行限制的控制元件。

1.2 佈局行為

LimitedBox是將child限制在其設定的最大寬高中的,但是這個限定是有條件的。當LimitedBox最大寬度不受限制時,child的寬度就會受到這個最大寬度的限制,同理高度。

1.3 繼承關係

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > LimitedBox
複製程式碼

1.4 示例程式碼

Row(
  children: <Widget>[
    Container(
      color: Colors.red,
      width: 100.0,
    ),
    LimitedBox(
      maxWidth: 150.0,
      child: Container(
        color: Colors.blue,
        width: 250.0,
      ),
    ),
  ],
)
複製程式碼

1.5 原始碼解析

const LimitedBox({
  Key key,
  this.maxWidth = double.infinity,
  this.maxHeight = double.infinity,
  Widget child,
})
複製程式碼

1.5.1 屬性解析

maxWidth:限定的最大寬度,預設值是double.infinity,不能為負數。

maxHeight:同上。

1.5.2 原始碼

先不說其原始碼,單純從其作用,前面介紹的SizedBox、ConstrainedBox都類似,都是通過強加到child的constraint,來達到相應的效果。

我們直接看其計算constraint的程式碼

minWidth: constraints.minWidth,
maxWidth: constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth),
minHeight: constraints.minHeight,
maxHeight: constraints.hasBoundedHeight ? constraints.maxHeight : constraints.constrainHeight(maxHeight)
複製程式碼

LimitedBox只是改變最大寬高的限定。具體的佈局程式碼如下:

if (child != null) {
  child.layout(_limitConstraints(constraints), parentUsesSize: true);
  size = constraints.constrain(child.size);
} else {
  size = _limitConstraints(constraints).constrain(Size.zero);
}
複製程式碼

根據最大尺寸,限制child的佈局,然後將自身調節到child的尺寸。

1.6 使用場景

使用場景是不可能清楚了,光是找例子,就花了不少時間。Flutter的一些冷門控制元件,真的是除了官方的文件,啥材料都木有。谷歌說這個很有用,還是一臉懵逼。這種控制元件,也有其他的替代解決方案,LimitedBox可以達到的效果,ConstrainedBox都可以實現。

2. Offstage

A widget that lays the child out as if it was in the tree, but without painting anything, without making the child available for hit testing, and without taking any room in the parent.

2.1 簡介

Offstage的作用很簡單,通過一個引數,來控制child是否顯示,日常使用中也算是比較常用的控制元件。

2.2 佈局行為

Offstage的佈局行為完全取決於其offstage引數

  • 當offstage為true,當前控制元件不會被繪製在螢幕上,不會響應點選事件,也不會佔用空間;
  • 當offstage為false,當前控制元件則跟平常用的控制元件一樣渲染繪製;

另外,當Offstage不可見的時候,如果child有動畫,應該手動停掉,Offstage並不會停掉動畫。

2.3 繼承關係

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Offstage
複製程式碼

2.4 示例程式碼

Column(
  children: <Widget>[
    new Offstage(
      offstage: offstage,
      child: Container(color: Colors.blue, height: 100.0),
    ),
    new CupertinoButton(
      child: Text("點選切換顯示"),
      onPressed: () {
        setState(() {
          offstage = !offstage;
        });
      },
    ),
  ],
)
複製程式碼

當點選切換按鈕的時候,可以看到Offstage顯示消失。

2.5 原始碼解析

const Offstage({ Key key, this.offstage = true, Widget child })
複製程式碼

2.5.1 屬性解析

offstage:預設為true,也就是不顯示,當為flase的時候,會顯示該控制元件。

2.5.2 原始碼

我們先來看下Offstage的computeIntrinsicSize相關的方法:

@override
double computeMinIntrinsicWidth(double height) {
    if (offstage)
      return 0.0;
    return super.computeMinIntrinsicWidth(height);
  }
複製程式碼

可以看到,當offstage為true的時候,自身的最小以及最大寬高都會被置為0.0。

接下來我們來看下其hitTest方法:

  @override
  bool hitTest(HitTestResult result, { Offset position }) {
    return !offstage && super.hitTest(result, position: position);
  }
複製程式碼

當offstage為true的時候,也不會去執行。

最後我們來看下其paint方法:

@override
  void paint(PaintingContext context, Offset offset) {
    if (offstage)
      return;
    super.paint(context, offset);
  }
複製程式碼

當offstage為true的時候直接返回,不繪製了。

到此,跟上面所說的佈局行為對應上了。我們一定要清楚一件事情,Offstage並不是通過插入或者刪除自己在widget tree中的節點,來達到顯示以及隱藏的效果,而是通過設定自身尺寸、不響應hitTest以及不繪製,來達到展示與隱藏的效果。

2.6 使用場景

當我們需要控制一個區域顯示或者隱藏的時候,可以使用這個場景。其實也有其他代替的方法,但是成本會高很多,例如直接在tree上插入刪除,但是不建議這麼做。

3. OverflowBox

A widget that imposes different constraints on its child than it gets from its parent, possibly allowing the child to overflow the parent.

3.1 簡介

OverflowBox這個控制元件,允許child超出parent的範圍顯示,當然不用這個控制元件,也有很多種方式實現類似的效果。

3.2 佈局行為

當OverflowBox的最大尺寸大於child的時候,child可以完整顯示,當其小於child的時候,則以最大尺寸為基準,當然,這個尺寸都是可以突破父節點的。最後加上對齊方式,完成佈局。

3.3 繼承關係

Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > OverflowBox
複製程式碼

3.4 示例程式碼

Container(
  color: Colors.green,
  width: 200.0,
  height: 200.0,
  padding: const EdgeInsets.all(5.0),
  child: OverflowBox(
    alignment: Alignment.topLeft,
    maxWidth: 300.0,
    maxHeight: 500.0,
    child: Container(
      color: Color(0x33FF00FF),
      width: 400.0,
      height: 400.0,
    ),
  ),
)
複製程式碼

OverflowBox例子

當maxHeight大於height的時候,可以完全顯示下來,當maxHeight小於height的時候,則不會會被隱藏掉

3.5 原始碼解析

建構函式如下:

const OverflowBox({
    Key key,
    this.alignment = Alignment.center,
    this.minWidth,
    this.maxWidth,
    this.minHeight,
    this.maxHeight,
    Widget child,
  })
複製程式碼

3.5.1 屬性解析

alignment:對齊方式。

minWidth:允許child的最小寬度。如果child寬度小於這個值,則按照最小寬度進行顯示。

maxWidth:允許child的最大寬度。如果child寬度大於這個值,則按照最大寬度進行展示。

minHeight:允許child的最小高度。如果child高度小於這個值,則按照最小高度進行顯示。

maxHeight:允許child的最大高度。如果child高度大於這個值,則按照最大高度進行展示。

其中,最小以及最大寬高度,如果為null的時候,就取父節點的constraint代替。

3.5.2 原始碼

OverflowBox的原始碼很簡單,我們先來看一下佈局程式碼:

if (child != null) {
  child.layout(_getInnerConstraints(constraints), parentUsesSize: true);
  alignChild();
}
複製程式碼

如果child不為null,child則會按照計算出的constraints進行尺寸的調整,然後對齊。

至於constraints的計算,則還是上面的邏輯,如果設定的有的話,就取這個值,如果沒有的話,就拿父節點的。

3.6 使用場景

有時候設計圖上出現的角標,會超出整個模組,可以使用OverflowBox控制元件。但我們應該知道,不使用這種控制元件,也可以完成佈局,在最外面包一層,也能達到一樣的效果。具體實施起來哪個比較方便,同學們自行取捨。

4. SizedBox

A box with a specified size.

4.1 簡介

比較常用的一個控制元件,設定具體尺寸。

4.2 佈局行為

SizedBox佈局行為相對較簡單:

  • child不為null時,如果設定了寬高,則會強制把child尺寸調到此寬高;如果沒有設定寬高,則會根據child尺寸進行調整;
  • child為null時,如果設定了寬高,則自身尺寸調整到此寬高值,如果沒設定,則尺寸為0;

4.3 繼承關係

Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > SizedBox
複製程式碼

4.4 示例程式碼

Container(
  color: Colors.green,
  padding: const EdgeInsets.all(5.0),
  child: SizedBox(
    width: 200.0,
    height: 200.0,
    child: Container(
      color: Colors.red,
      width: 100.0,
      height: 300.0,
    ),
  ),
)
複製程式碼

4.5 原始碼解析

建構函式

const SizedBox({ Key key, this.width, this.height, Widget child })
複製程式碼

4.5.1 屬性解析

width:寬度值,如果具體設定了,則強制child寬度為此值,如果沒設定,則根據child寬度調整自身寬度。

height:同上。

4.5.2 原始碼

SizedBox內部是通過RenderConstrainedBox來實現的。具體的原始碼就不解析了,總體思路是,根據寬高值算好一個constraints,然後強制應用到child上。

4.6 使用場景

這個控制元件,很多場景可以使用。但是,可以替代它的控制元件也有不少,例如Container、ConstrainedBox等。而且SizedBox就是ConstrainedBox的一個特例。還是那句話,精簡啊,多提供一些常用的,不要提供一大堆重複的,場景很少的控制元件。

5. 後話

筆者建了一個Flutter學習相關的專案,Github地址,裡面包含了筆者寫的關於Flutter學習相關的一些文章,會定期更新,文章中的程式碼也在這個專案中,歡迎大家關注。

6. 參考

  1. LimitedBox class
  2. Offstage class
  3. OverflowBox class
  4. SizedBox class

相關文章