本文主要介紹Flutter佈局中的LimitedBox、Offstage、OverflowBox、SizedBox四種控制項,詳細介紹了其佈局行為以及使用場景,並對源碼進行了分析。 ...
本文主要介紹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,
),
),
)
當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學習相關的一些文章,會定期更新,文章中的代碼也在這個項目中,歡迎大家關註。