天天看點

【flutter】flutter Widget核心點總結大全

1、Widget

Widget是個抽象類,定義如下:

@immutable
abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });
  final Key key;

  @protected
  Element createElement();

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}
           

說明:

(1)Key: 這個key屬性主要的作用是決定是否在下一次build時複用舊的widget,決定的條件在canUpdate()方法中。

(2)canUpdate(...)是一個靜态方法,它主要用于在Widget樹重新build時複用舊的widget。隻要newWidget與oldWidget的runtimeType和key同時相等時就會用newWidget去更新Element對象的配置,否則就會建立新的Element。

(3)StatelessWidget和StatefulWidget是Widget常用的兩個子類,他們兩也是abstract抽象類。

2、StatefulWidget

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);

  @override
  StatefulElement createElement() => new StatefulElement(this);

  @protected
  State createState();
}
           

說明:

(1)一個StatefulWidget類會對應一個State類,State表示與其對應的StatefulWidget要維護的狀态。本質上就是一個StatefulElement對應一個State執行個體。

(2)當State被改變時,可以手動調用其setState()方法通知Flutter framework狀态發生改變,Flutter framework在收到消息後,會重新調用其build方法重新建構widget樹,進而達到更新UI的目的。

3、State生命周期

(1)舉個生命周期例子

(a)首次啟動StatefulWidget,生命周期執行如下:

initState 
didChangeDependencies
build
           

(b)然後點選熱重載按鈕,生命周期執行如下:

reassemble
didUpdateWidget
build
           

(c)在widget樹中将目前StatefulWidget移除,然後熱重載,生命周期執行如下:

reassemble 
deactivate
dispose
           

(2)生命周期方法說明

(a)initState:當Widget第一次插入到Widget樹時會被調用,Flutter framework隻會調用一次該回調。

(b)didChangeDependencies:當State對象的依賴發生變化時會被調用。

(c)build:用于建構Widget子樹。

(d)reassemble:此回調是專門為了開發調試而提供的,在熱重載(hot reload)時會被調用,此回調在Release模式下永遠不會被調用。

(e)didUpdateWidget:在widget重新建構時,如果Widget.canUpdate傳回true則會調用此回調。

(f)deactivate:當State對象從樹中被移除時,會調用此回調。如果移除後沒有重新插入到樹中則緊接着會調用dispose()方法。

(g) dispose:當State對象從樹中被永久移除時調用;通常在此回調中釋放資源。

注意:在繼承StatefulWidget重寫其方法時,對于包含@mustCallSuper标注的父類方法,都要在子類方法中先調用父類方法。

4、狀态管理

最常見的狀态管理方法如下:

(1)Widget管理自己的state。

該Widget本身是StatefulWidget。

(2)父widget管理子widget狀态。

父類為StatefulWidget, 子類為StatelessWidget。子類通過回調将其狀态導出到其父項。

(3)混合管理(父widget和子widget都管理狀态)。

此時父類和子類都繼承于StatefulWidget。

5、全局狀态管理

當應用中包括一些跨widget(甚至跨路由)的狀态需要同步時,上面介紹的方法很難勝任了。比如,我們有一個設定頁,裡面可以設定應用語言,但是我們為了讓設定實時生效,我們期望在語言狀态發生改變時,我們的APP Widget能夠重新build一下,但我們的APP Widget和設定頁并不在一起。正确的做法是通過一個全局狀态管理器來處理這種“相距較遠”的widget之間的通信。

目前主要方法:實作一個全局的事件總線,将語言狀态改變對應為一個事件,然後在APP Widget所在的父widget initState 方法中訂閱語言改變的事件,當使用者在設定頁切換語言後,我們觸發語言改變事件,然後APP Widget那邊就會收到通知,然後重新build一下即可。

6、基礎widget

(1)Text:可以建立一個帶格式的文本。

(2)Row、 Column:可讓在水準(Row)和垂直(Column)方向上建立靈活的布局。

(3)Stack:(和Android中的FrameLayout相似),Stack允許子 widget 堆疊, 你可以使用 Positioned 來定位他們相對于Stack的上下左右四條邊的位置。Stacks是基于Web開發中的絕對定位(absolute positioning )布局模型設計的。

(4)Container:Container可以建立矩形視覺元素。container 可以裝飾一個BoxDecoration, 如 background、一個邊框、或者一個陰影。 Container 也可以具有邊距(margins)、填充(padding)和應用于其大小的限制(constraints)。另外, Container可以使用矩陣在三維空間中對其進行變換。

7、按鈕

Material widget庫中提供了多種按鈕Widget如RaisedButton、FlatButton、OutlineButton等。

所有Material 庫中的按鈕都有如下相同點:按下時都會有“水波動畫”;有一個onPressed屬性來設定點選回調。

8、圖檔

(1)在Flutter中,我們可以通過Image來加載并顯示圖檔,Image的資料源可以是asset、檔案、記憶體以及網絡。

(2)ImageProvider

ImageProvider 是一個抽象類,主要定義了圖檔資料擷取的接口load()。從不同的資料源擷取圖檔需要實作不同的ImageProvider,如AssetImage是實作了從Asset中加載圖檔的ImageProvider,而NetworkImage實作了從網絡加載圖檔的ImageProvider。

(3)Image widget有一個必選的image參數,它對應一個ImageProvider。

9、字型圖示

(1)Flutter中,可以像web開發一樣使用iconfont“字型圖示”,它是将圖示做成字型檔案,然後通過指定不同的字元而顯示不同的圖檔。

(2)在Flutter開發中,iconfont和圖檔相比有如下優勢:

體積小:可以減小安裝包大小。

矢量的:iconfont都是矢量圖示,放大不會影響其清晰度。

可以應用文本樣式:可以像文本一樣改變字型圖示的顔色、大小對齊等。

可以通過TextSpan和文本混用。

10、單選開關Switch和複選框Checkbox

單選開關Switch和複選框Checkbox,它們都是繼承自StatelessWidget,是以它們本身不會儲存目前選擇狀态,是以一般都是在父widget中管理選中狀态。當使用者點選Switch或Checkbox時,它們會觸發onChanged回調,我們可以在此回調中處理選中狀态改變邏輯。

11、輸入框TextField

(1)監聽文本變化的方式有:onChange回調、controller監聽。

(2)控制焦點

焦點可以通過FocusNode和FocusScopeNode來控制,預設情況下,焦點由FocusScope來管理,它代表焦點控制範圍,可以在這個範圍内可以通過FocusScopeNode在輸入框之間移動焦點、設定預設焦點等。我們可以通過FocusScope.of(context) 來擷取widget樹中預設的FocusScopeNode。

12、表單Form

(1)可以對輸入框資料進行合法性校驗。

(2)可以對一組輸入框進行統一處理。

13、布局類Widget概述

Flutter中,根據Widget是否需要包含子節點将Widget分為了三類:

LeafRenderObjectWidget:沒有子節點的widget。

SingleChildRenderObjectWidget:包含一個子widget。

MultiChildRenderObjectWidget:包含多個子Widget,一般都有一個children參數。

舉幾個布局類Widget:

(1)線性布局Row/Column

(2)彈性布局(Flex和Expanded配合)

(3)流式布局Wrap/Flow

(4)層疊布局Stack(使用Positioned定位)

14、Row/Column(線性布局)

(1)Row和(水準布局)Column(垂直布局)都繼承自Flex(彈性布局)。

(2)對于線性布局,有主軸和縱軸之分,如果布局是沿水準方向,那麼主軸就是指水準方向,而縱軸即垂直方向;如果布局沿垂直方向,那麼主軸就是指垂直方向,而縱軸就是水準方向。線上性布局中,有兩個定義對齊方式的枚舉類MainAxisAlignment和CrossAxisAlignment,分别代表主軸對齊和縱軸對齊。

15、Flex和Expanded配合(彈性布局)

const Expanded({
  int flex = 1, 
  @required Widget child,
})
           

flex為彈性系數,如果為0或null,則child是沒有彈性的,即不會被擴伸占用的空間。如果大于0,所有的Expanded按照其flex的比例來分割Flex主軸的全部空閑空間。例如:

//Flex的兩個子widget按1:2來占據水準空間  
Flex(
  direction: Axis.horizontal,
  children: <Widget>[
    Expanded(
      flex: 1,
      child: Container(
        height: 30.0,
        color: Colors.red,
      ),
    ),
    Expanded(
      flex: 2,
      child: Container(
        height: 30.0,
        color: Colors.green,
      ),
    ),
  ],
),
           

16、Wrap和Flow(流式布局)

我們把超出螢幕顯示範圍會自動折行的布局稱為流式布局。Flutter中通過Wrap和Flow來支援流式布局。

17、容器類Widget和布局類Widget差別

布局類Widget一般都需要接收一個widget數組(children),他們直接或間接繼承自(或包含)MultiChildRenderObjectWidget ;而容器類Widget一般隻需要接收一個子Widget(child),他們直接或間接繼承自(或包含)SingleChildRenderObjectWidget。

舉幾個容器類Widget:

(1)padding

(2)限制類容器ConstrainedBox/SizedBox

ConstrainedBox用于對子widget添加額外的限制。

(3)裝飾容器DecoratedBox

(4)變換Transform

Transform可以在其子Widget繪制時對其應用一個矩陣變換。

注意:Transform的變換是應用在繪制階段,而并不是應用在布局(layout)階段,是以無論對子widget應用何種變化,其占用空間的大小和在螢幕上的位置都是固定不變的,因為這些是在布局階段就确定的。

(5)Container

18、可滾動的widget

(1)滾動條Scrollbar

如果要給可滾動widget添加滾動條,隻需将Scrollbar作為可滾動widget的父widget即可。

Scrollbar(
  child: SingleChildScrollView(
    ...
  ),
);
           

(2)SingleChildScrollView

SingleChildScrollView類似于Android中的ScrollView,它隻能接收一個子Widget。

(3)ListView

如何自動拉升ListView以填充螢幕剩餘空間呢?答案是使用Flex。前面已經介紹過在Flex布局中,可以使用Expanded自動拉伸元件大小的Widget,我們也說過Column是繼承自Flex的,是以我們可以直接使用Column+Expanded來實作,代碼如下:

@override
Widget build(BuildContext context) {
  return Column(children: <Widget>[
    ListTile(title:Text("商品清單")),
    Expanded(
      child: ListView.builder(itemBuilder: (BuildContext context, int index) {
        return ListTile(title: Text("$index"));
      }),
    ),
  ]);
}
           

(4)GridView

(5)CustomScrollView自定義滾動模型

CustomScrollView是可以使用sliver來自定義滾動模型(效果)的widget。它可以包含多種滾動模型,舉個例子,假設有一個頁面,頂部需要一個GridView,底部需要一個ListView,而要求整個頁面的滑動效果是統一的,即它們看起來是一個整體,如果使用GridView+ListView來實作的話,就不能保證一緻的滑動效果,因為它們的滾動效果是分離的,是以這時就需要一個"膠水",把這些彼此獨立的可滾動widget(Sliver)"粘"起來,而CustomScrollView的功能就相當于“膠水”。

在Flutter中,Sliver通常指具有特定滾動效果的可滾動塊。可滾動widget,如ListView、GridView等都有對應的Sliver實作如SliverList、SliverGrid等。對于大多數Sliver來說,它們和可滾動Widget最主要的差別是Sliver不會包含Scrollable Widget,也就是說Sliver本身不包含滾動互動模型 ,正因如此,CustomScrollView才可以将多個Sliver"粘"在一起,這些Sliver共用CustomScrollView的Scrollable,最終實作統一的滑動效果。