Flutter 布局(一)- Container詳解

Q吹個大氣球Q
關注
62018.06.03 18:36:26字數 2,301閱讀 84,509
本文主要介紹Flutter中非常常見的Container,列舉了一些實際例子介紹如何使用。
1. 簡介
A convenience widget that combines common painting, positioning, and sizing widgets.
Container在Flutter中太常見了。官方給出的簡介,是一個結合了繪制(painting)、定位(positioning)以及尺寸(sizing)widget的widget。
可以得出幾個資訊,它是一個組合的widget,内部有繪制widget、定位widget、尺寸widget。後續看到的不少widget,都是通過一些更基礎的widget組合而成的。
1.1 組成
Container的組成如下:
- 最裡層的是child元素;
- child元素首先會被padding包着;
- 然後添加額外的constraints限制;
- 最後添加margin。
Container的繪制的過程如下:
- 首先會繪制transform效果;
- 接着繪制decoration;
- 然後繪制child;
- 最後繪制foregroundDecoration。
Container自身尺寸的調節分兩種情況:
- Container在沒有子節點(children)的時候,會試圖去變得足夠大。除非constraints是unbounded限制,在這種情況下,Container會試圖去變得足夠小。
- 帶子節點的Container,會根據子節點尺寸調節自身尺寸,但是Container構造器中如果包含了width、height以及constraints,則會按照構造器中的參數來進行尺寸的調節。
1.2 布局行為
由于Container組合了一系列的widget,這些widget都有自己的布局行為,是以Container的布局行為有時候是比較複雜的。
一般情況下,Container會遵循如下順序去嘗試布局:
- 對齊(alignment);
- 調節自身尺寸适合子節點;
- 采用width、height以及constraints布局;
- 擴充自身去适應父節點;
- 調節自身到足夠小。
進一步說:
- 如果沒有子節點、沒有設定width、height以及constraints,并且父節點沒有設定unbounded的限制,Container會将自身調整到足夠小。
- 如果沒有子節點、對齊方式(alignment),但是提供了width、height或者constraints,那麼Container會根據自身以及父節點的限制,将自身調節到足夠小。
- 如果沒有子節點、width、height、constraints以及alignment,但是父節點提供了bounded限制,那麼Container會按照父節點的限制,将自身調整到足夠大。
- 如果有alignment,父節點提供了unbounded限制,那麼Container将會調節自身尺寸來包住child;
- 如果有alignment,并且父節點提供了bounded限制,那麼Container會将自身調整的足夠大(在父節點的範圍内),然後将child根據alignment調整位置;
- 含有child,但是沒有width、height、constraints以及alignment,Container會将父節點的constraints傳遞給child,并且根據child調整自身。
另外,margin以及padding屬性也會影響到布局。
1.3 繼承關系
Object > Diagnosticable > DiagnosticableTree > Widget > StatelessWidget > Container
從繼承關系可以看出,Container是一個StatelessWidget。Container并不是一個最基礎的widget,它是由一系列的基礎widget組合而成。
2. 源碼解析
構造函數如下:
Container({
Key key,
this.alignment,
this.padding,
Color color,
Decoration decoration,
this.foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
this.margin,
this.transform,
this.child,
})
平時使用最多的,也就是padding、color、width、height、margin屬性。
2.1 屬性解析
key:Container唯一辨別符,用于查找更新。
alignment:控制child的對齊方式,如果container或者container父節點尺寸大于child的尺寸,這個屬性設定會起作用,有很多種對齊方式。
padding:decoration内部的空白區域,如果有child的話,child位于padding内部。padding與margin的不同之處在于,padding是包含在content内,而margin則是外部邊界,設定點選事件的話,padding區域會響應,而margin區域不會響應。
color:用來設定container背景色,如果foregroundDecoration設定的話,可能會遮蓋color效果。
decoration:繪制在child後面的裝飾,設定了decoration的話,就不能設定color屬性,否則會報錯,此時應該在decoration中進行顔色的設定。
foregroundDecoration:繪制在child前面的裝飾。
width:container的寬度,設定為double.infinity可以強制在寬度上撐滿,不設定,則根據child和父節點兩者一起布局。
height:container的高度,設定為double.infinity可以強制在高度上撐滿。
constraints:添加到child上額外的限制條件。
margin:圍繞在decoration和child之外的空白區域,不屬于内容區域。
transform:設定container的變換矩陣,類型為Matrix4。
child:container中的内容widget。
2.2 一個例子
new Container(
constraints: new BoxConstraints.expand(
height:Theme.of(context).textTheme.display1.fontSize * 1.1 + 200.0,
),
decoration: new BoxDecoration(
border: new Border.all(width: 2.0, color: Colors.red),
color: Colors.grey,
borderRadius: new BorderRadius.all(new Radius.circular(20.0)),
image: new DecorationImage(
image: new NetworkImage('http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f06c.jpg'),
centerSlice: new Rect.fromLTRB(270.0, 180.0, 1360.0, 730.0),
),
),
padding: const EdgeInsets.all(8.0),
alignment: Alignment.center,
child: new Text('Hello World',
style: Theme.of(context).textTheme.display1.copyWith(color: Colors.black)),
transform: new Matrix4.rotationZ(0.3),
)
這是官方文檔給出例子的一個變種,包含屬性比較全,可以看下其用法。實際運作效果如下:
Container屬性用法
其中decoration可以設定邊框、背景色、背景圖檔、圓角等屬性,非常實用。對于transform這個屬性,一般有過其他平台開發經驗的,都大緻了解,這種變換,一般不是變換的實際位置,而是變換的繪制效果,也就是說它的點選以及尺寸、間距等都是按照未變換前的。
2.3 源碼
decoration = decoration ?? (color != null ? new BoxDecoration(color: color) : null),
可以看出,對于顔色的設定,最後都是轉換為decoration來進行繪制的。如果同時包含decoration和color兩種屬性,則會報錯。
@override
Widget build(BuildContext context) {
Widget current = child;
if (child == null && (constraints == null || !constraints.isTight)) {
current = new LimitedBox(
maxWidth: 0.0,
maxHeight: 0.0,
child: new ConstrainedBox(constraints: const BoxConstraints.expand())
);
}
if (alignment != null)
current = new Align(alignment: alignment, child: current);
final EdgeInsetsGeometry effectivePadding = _paddingIncludingDecoration;
if (effectivePadding != null)
current = new Padding(padding: effectivePadding, child: current);
if (decoration != null)
current = new DecoratedBox(decoration: decoration, child: current);
if (foregroundDecoration != null) {
current = new DecoratedBox(
decoration: foregroundDecoration,
position: DecorationPosition.foreground,
child: current
);
}
if (constraints != null)
current = new ConstrainedBox(constraints: constraints, child: current);
if (margin != null)
current = new Padding(padding: margin, child: current);
if (transform != null)
current = new Transform(transform: transform, child: current);
return current;
}
Container的build函數不長,繪制也是一個線性的判斷的過程,一層一層的包裹着widget,去實作不同的樣式。
最裡層的是child,如果為空或者其他限制條件,則最裡層包含的為一個LimitedBox,然後依次是Align、Padding、DecoratedBox、前景DecoratedBox、ConstrainedBox、Padding(實作margin效果)、Transform。
Container的源碼本身并不複雜,複雜的是它的各種布局表現。我們謹記住一點,如果内部不設定限制,則按照父節點盡可能的擴大,如果内部有限制,則按照内部來。
2.4 使用場景
Container算是目前項目中,最經常用到的一個widget。在實際使用過程中,筆者在以下情況會使用到Container,當然并不是絕對的,也可以通過其他widget來實作。
- 需要設定間隔(這種情況下,如果隻是單純的間隔,也可以通過Padding來實作);
- 需要設定背景色;
- 需要設定圓角或者邊框的時候(ClipRRect也可以實作圓角效果);
- 需要對齊(Align也可以實作);
- 需要設定背景圖檔的時候(也可以使用Stack實作)。
3. 例子
接下來我們試着去做一個圓角按鈕,它包含以下特性:
- 支援設定按鈕的三種狀态(正常态、點選态、禁用态)的色值;
- 支援設定按鈕标題;
- 支援設定寬高;
- 支援點選回調;
根據上面介紹,利用decoration這個屬性,基本上就可以完成效果了,至于點選效果以及點選回調,則使用一個GestureDetector就可以完成了。實際的例子非常簡單,在這裡就不貼代碼了。實際運作效果如下所示:
Container樣例
3.1 注意事項
這個小控件,寫起來很簡單,本身沒有什麼難度,隻是純粹的介紹了Container的使用方法,但是有一個地方需要注意的。在控件的deactivate狀态,我們需要将控件的屬性初始到最開始的狀态,例如在本例中,有如下代碼:
@override
void deactivate() {
super.deactivate();
currentColor = widget.backgroundColor;
}
這麼做是為什麼了?是因為在點選按鈕進行頁面跳轉的時候,按鈕處在點選态,當我們傳回的時候,頁面還是處在點選态,這顯然就不正确了,是以需要我們手動的在deactivate狀态下,将控件恢複到初始狀态。但是呢,這個設定顔色,并不是說在deactivate的時候,就立馬去重新整理控件,而是在下次再進入這個頁面的時候,再次運作build的時候,會按照這個初始值進行繪制,也就是恢複到了最開始的狀态。
3.2 代碼
代碼Github位址,這是一個系列的項目,如果不出意外,會将Flutter中常見的二十多種布局widget都介紹一下。
4. 後話
筆者建了一個flutter學習相關的項目,github位址,裡面包含了筆者寫的關于flutter學習相關的一些文章,會定期更新,也會上傳一些學習demo,歡迎大家關注。
5. 參考
- Container class