0. 前言
在 Flutter 當中,我們如何控制元件的大小?套上一層 Container?SizeBox?還是一些別的技巧?
有沒有同學遇到這種情況:在佈局中,無論如何都控制不了一個元件的大小,煩的一批?
例如這段程式碼:
Widget build(BuildContext context) {
return Scaffold(
body: ListView.separated(itemBuilder: (context, index){
return Container(
height: 200,
width: 200,
color: Colors.amber,
);
}, itemCount: 10, separatorBuilder: (BuildContext context, int index) {
return Container(height: 10,);
},),
);
}
複製程式碼
我建立了一個ListView,每一個 item 是一個 200*200 的琥鉑色(黃色)Container,講道理,這個時候我們的腦海裡就應該有這樣的一個列表呈現。然而,當你執行的時候:
WTF??我 200*200 的大方塊呢?怎麼變成長方形了?
難道不應該是這樣的嗎?
好,帶著這個問題,我們就來說今天的兩個主角。
注意:這裡並不打算深入討論 Flutter 中約束的機制,只是為了講解這兩個控制元件。
看本篇文章時我們只需要記住:child 尺寸大小是由父級 Widget 給出的約束來調整的。
其他的如果想要了解,可以看這篇處理邊界約束 (Box constraints) 的問題
1. ConstrainedBox
首先我們來說一下 ConstrainedBox
,從名字上我們也能解讀個大概:「約束盒」。
沒錯,就是約束盒,他是用來幹嘛的?看一下官方文件:
A widget that imposes additional constraints on its child.
For example, if you wanted child to have a minimum height of 50.0 logical pixels, you could use const BoxConstraints(minHeight: 50.0) as the constraints.
在其 child 上新增約束的 widget。
例如,如果你希望 child 最小高度為 50.0 畫素,可以用 BoxConstraints(minHeight: 50.0) 來進行約束。
接下來再看一下他的建構函式:
ConstrainedBox({
Key key,
@required this.constraints,
Widget child,
}) : assert(constraints != null),
assert(constraints.debugAssertIsValid()),
super(key: key, child: child);
複製程式碼
可以看到,必要的引數有 constraints: BoxConstraints
,這裡也簡單說一下約束:
約束是由最小寬度、最大寬度、最小高度、最大高度四個方面構成;尺寸大小則由特定的寬度和高度兩個方面構成。
那我們再來看一下 BoxConstraints
是怎麼用的,它有6個建構函式,分別是:
BoxConstraints({double minWidth: 0.0, double maxWidth: double.infinity, double minHeight: 0.0, double maxHeight: double.infinity })
:使用給定的約束來構建BoxConstraints.expand({double width, double height })
:建立一個充滿另一個約束的約束BoxConstraints.expand({double width, double height })
:建立一個不能大於給定大小的約束BoxConstraints.tight(Size size)
:建立一個給定大小的約束BoxConstraints.tightFor({double width, double height })
:同上BoxConstraints.tightForFinite({double width: double.infinity, double height: double.infinity })
:建立給定大小的約束,除非他們是無限的
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 200,
width: 200,
color: Colors.amber,
),
),
);
}
複製程式碼
這是一個 200*200 的Container,接下來定義一個 ConstrainedBox
,並且把min寬高設定為 300*300:
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ConstrainedBox(
child: Container(
height: 200,
width: 200,
color: Colors.amber,
), constraints: BoxConstraints(minHeight: 300, minWidth: 300),
),
),
);
}
複製程式碼
可以看到,這個時候 Container 的寬高完全是按照 ConstrainedBox
的約束來設定的。
2. UnconstrainedBox
該元件就厲害了,官方解釋為:
A widget that imposes no constraints on its child, allowing it to render at its "natural" size.
This allows a child to render at the size it would render if it were alone on an infinite canvas with no constraints. This container will then attempt to adopt the same size, within the limits of its own constraints. If it ends up with a different size, it will align the child based on alignment. If the box cannot expand enough to accommodate the entire child, the child will be clipped.
對 child 不新增任何約束,允許他以 “自然”大小進行渲染。
這樣一來,child就可以在沒有約束的、無限的畫布上進行渲染,然後此容器講嘗試在自身限制的範圍內採用相同的大小,如果大小不相同,則根據 alignment 來對齊,如果child過大,則會裁剪 child。
在我們開頭舉的例子,為什麼我設定 200*200 的Container預設是螢幕寬度的?
因為ListView這種型別的元件會根據滑動方向來設定約束。
例如垂直滑動,那麼所有 item 的寬度都是預設 double.infinity
,所以這個時候無論我們設定多少寬度都是無用的,
相反也一樣。
所以,如果我們想自己設定 item 的寬度,就需要用到 UnconstrainedBox
,不給child 設定約束。
3. 總結
這兩個元件實際開發過程中使用的可能不是很多,但是瞭解一下肯定是要的,否則遇到類似的問題也很麻煩。
總結來講,這些都屬於 Flutter 約束中的知識點,只要有個瞭解就好了。
關於「網易雲音樂專案」的文章,後續也會有,不過最近有點忙,程式碼還是會更新的,請大家放心。
另我個人建立了一個「Flutter 交流群」,可以新增我個人微信 「17610912320」來入群。