Android當中,一切都是View。佈局類View稱為ViewGroup。Flutter當中,一切都是Widget。
而根據Widget是否需要包含子節點將Widget分為了三類:
- 沒有子節點的widget
- 只有一個孩子的widget
- 包含多個孩子的widget
而我們在前文中也提到過,Element樹才是最終的繪製樹,Element樹是通過widget樹來建立的(通過Widget.createElement()
),widget其實就是Element的配置資料。
Flutter中,根據Widget是否需要包含子節點將Widget分為了三類,分別對應三種Element,如下表:
Widget | 對應的Element | 用途 |
---|---|---|
LeafRenderObjectWidget | LeafRenderObjectElement | Widget樹的葉子節點,用於沒有子節點的widget,通常基礎widget都屬於這一類,如Text、Image。 |
SingleChildRenderObjectWidget | SingleChildRenderObjectElement | 包含一個子Widget,如:ConstrainedBox、DecoratedBox等 |
MultiChildRenderObjectWidget | MultiChildRenderObjectElement | 包含多個子Widget,一般都有一個children引數,接受一個Widget陣列。如Row、Column、Stack等 |
注意,Flutter中的很多Widget是直接繼承自StatelessWidget或StatefulWidget,然後在build()方法中構建真正的RenderObjectWidget,如Text,它其實是繼承自StatelessWidget,然後在build()方法中通過RichText來構建其子樹,而RichText才是繼承自LeafRenderObjectWidget。所以為了方便敘述,我們也可以直接說Text屬於LeafRenderObjectWidget(其它widget也可以這麼描述),這才是本質。讀到這裡我們也會發現,其實StatelessWidget和StatefulWidget就是兩個用於組合Widget的基類,它們本身並不關聯最終的渲染物件(RenderObjectWidget)。
基礎Widget簡介
與原生開發中“控制元件”不同的是,Flutter中的widget的概念更廣泛,它不僅可以表示UI元素,也可以表示一些功能性的元件如:用於手勢檢測的 GestureDetector widget、用於應用主題資料傳遞的Theme等等。
這裡特指直接呈現UI的widget,比如文字、按鈕、圖片、Icon、輸入框表單等等。
比如,Material widget庫中提供了多種按鈕Widget如RaisedButton、FlatButton。所有Material 庫中的按鈕都有如下相同點:
- 按下時都會有“水波動畫”。
- 有一個onPressed屬性來設定點選回撥,當按鈕按下時會執行該回撥,如果不提供該回撥則按鈕會處於禁用狀態,禁用狀態不響應使用者點選。
RaisedButton(
child: Text("normal"),
onPressed: () => {},
),
FlatButton(
child: Text("normal"),
onPressed: () => {},
),
OutlineButton(
child: Text("normal"),
onPressed: () => {},
),
IconButton(
icon: Icon(Icons.send),
onPressed: () => {},
),
FlatButton(
child: Text("Submit"),
color: Colors.blue,
highlightColor: Colors.blue[700],
colorBrightness: Brightness.dark,
textColor: Colors.white,
padding: EdgeInsets.all(10),
shape: RoundedRectangleBorder(
borderRadius : BorderRadius.circular(20)
),
splashColor: Colors.grey,
onPressed: (){},
),
複製程式碼
對應的效果如下:
佈局類Widget
直接或間接繼承(包含)MultiChildRenderObjectWidget的Widget,它們一般都會有一個children屬性用於接收子Widget。
比如 線性佈局Row和Column、彈性佈局Flex、流式佈局Wrap、Flow、層疊佈局Stack、Positioned等。
這裡以Wrap舉例。Flutter中,子widget超出螢幕範圍,則會報溢位錯誤。
可以看到,右邊溢位部分報錯。這是因為Row預設只有一行,如果超出螢幕不會折行。我們把超出螢幕顯示範圍會自動折行的佈局稱為流式佈局。Flutter中通過Wrap和Flow來支援流式佈局,將上例中的Row換成Wrap後溢位部分則會自動折行。下面是Wrap的定義:
Wrap({
...
this.direction = Axis.horizontal,
this.alignment = WrapAlignment.start,
this.spacing = 0.0,
this.runAlignment = WrapAlignment.start,
this.runSpacing = 0.0,
this.crossAxisAlignment = WrapCrossAlignment.start,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
List<Widget> children = const <Widget>[],
})
複製程式碼
樣例程式碼如下:
Wrap(
spacing: 8.0, // 主軸(水平)方向間距
runSpacing: 4.0, // 縱軸(垂直)方向間距
alignment: WrapAlignment.center, //沿主軸方向居中
children: <Widget>[
new Chip(
avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('A')),
label: new Text('Hamilton'),
),
new Chip(
avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('M')),
label: new Text('Lafayette'),
),
new Chip(
avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('H')),
label: new Text('Mulligan'),
),
new Chip(
avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('J')),
label: new Text('Laurens'),
),
],
)
複製程式碼
效果如下圖:
容器類Widget
直接或間接繼承(包含)SingleChildRenderObjectWidget的Widget,它們一般都會有一個child屬性用於接收子Widget。 這一類的widget有Padding、ConstrainedBox、DecoratedBox等。
容器類Widget和佈局類Widget都作用於其子Widget,不同的是:
-
佈局類Widget一般都需要接收一個widget陣列(children),他們直接或間接繼承自(或包含)MultiChildRenderObjectWidget.
佈局類Widget是按照一定的排列方式來對其子Widget進行排列; -
容器類Widget一般只需要接收一個子Widget(child),他們直接或間接繼承自(或包含)SingleChildRenderObjectWidget。
容器類Widget一般只是包裝其子Widget,對其新增一些修飾(補白或背景色等)、變換(旋轉或剪裁等)、或限制(大小等)。
更多widget瞭解可以跳轉這裡
如果你覺得這篇文章對你有益,還請幫忙轉發和點贊,萬分感謝。