<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## Widget簡介 ### 概念 在前面的介紹中,我們知道Flutter中幾乎所有的對象都是一個Widget,與原生開發中“控件”不同的是,Flutter中的widget的概念更廣泛,它不僅可以表示UI元素,也可以表示一些功能性的組件如:用于手勢檢測的 `GestureDetector` widget、用于應用主題數據傳遞的`Theme`等等。而原生開發中的控件通常只是指UI元素。在后面的內容中,我們在描述UI元素時,我們可能會用到“控件”、“組件”這樣的概念,讀者心里需要知道他們就是widget,只是在不同場景的不同表述而已。由于Flutter主要就是用于構建用戶界面的,所以,在大多數時候,讀者可以認為widget就是一個控件,不必糾結于概念。 ### Widget與Element 在Flutter中,Widget的功能是“描述一個UI元素的配置數據”,它就是說,Widget其實并不是表示最終繪制在設備屏幕上的顯示元素,而只是顯示元素的一個配置數據。實際上,Flutter中真正代表屏幕上顯示元素的類是`Element`,也就是說Widget只是描述`Element`的一個配置,有關`Element`的詳細介紹我們將在本書后面的高級部分深入介紹,讀者現在只需要知道,Widget只是UI元素的一個配置數據,并且一個Widget可以對應多個`Element`,這是因為同一個Widget對象可以被添加到UI樹的不同部分,而真正渲染時,UI樹的每一個Widget節點都會對應一個`Element`對象。總結一下: - Widget實際上就是Element的配置數據,Widget樹實際上是一個配置樹,而真正的UI渲染樹是由Element構成;不過,由于Element是通過Widget生成,所以它們之間有對應關系,所以在大多數場景,我們可以寬泛地認為Widget樹就是指UI控件樹或UI渲染樹。 - 一個Widget對象可以對應多個Element對象。這很好理解,根據同一份配置(Widget),可以創建多個實例(Element)。 讀者應該將這兩點牢記在心中。 ### 主要接口 我們先來看一下Widget類的聲明: ``` @immutable abstract class Widget extends DiagnosticableTree { const Widget({ this.key }); final Key key; @protected Element createElement(); @override String toStringShort() { return key == null ? '$runtimeType' : '$runtimeType-$key'; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense; } static bool canUpdate(Widget oldWidget, Widget newWidget) { return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; } } ``` - `Widget`類繼承自`DiagnosticableTree`,`DiagnosticableTree`即“診斷樹”,主要作用是提供調試信息。 - `Key`: 這個`key`屬性類似于React/Vue中的`key`,主要的作用是決定是否在下一次`build`時復用舊的widget,決定的條件在`canUpdate()`方法中。 - `createElement()`:正如前文所述“一個Widget可以對應多個`Element`”;Flutter Framework在構建UI樹時,會先調用此方法生成對應節點的`Element`對象。此方法是Flutter Framework隱式調用的,在我們開發過程中基本不會調用到。 - `debugFillProperties(...)` 復寫父類的方法,主要是設置診斷樹的一些特性。 - `canUpdate(...)`是一個靜態方法,它主要用于在Widget樹重新`build`時復用舊的widget,其實具體來說,應該是:是否用新的Widget對象去更新舊UI樹上所對應的`Element`對象的配置;通過其源碼我們可以看到,只要`newWidget`與`oldWidget`的`runtimeType`和`key`同時相等時就會用`newWidget`去更新`Element`對象的配置,否則就會創建新的`Element`。 有關Key和Widget復用的細節將會在本書后面高級部分深入討論,讀者現在只需知道,為Widget顯式添加key的話可能(但不一定)會使UI在重新構建時變的高效,讀者目前可以先忽略此參數。本書后面的示例中,我們只在構建列表項UI時會顯式指定Key。 另外`Widget`類本身是一個抽象類,其中最核心的就是定義了`createElement()`接口,在Flutter開發中,我們一般都不用直接繼承`Widget`類來實現Widget,相反,我們通常會通過繼承`StatelessWidget`和`StatefulWidget`來間接繼承`Widget`類來實現,而`StatelessWidget`和`StatefulWidget`都是直接繼承自`Widget`類,而這兩個類也正是Flutter中非常重要的兩個抽象類,它們引入了兩種Widget模型,接下來我們將重點介紹一下這兩個類。 ## Stateless Widget 在之前的章節中,我們已經簡單介紹過StatelessWidget,StatelessWidget相對比較簡單,它繼承自`Widget`,重寫了`createElement()`方法: ``` @override StatelessElement createElement() => new StatelessElement(this); ``` `StatelessElement` 間接繼承自`Element`類,與StatelessWidget相對應(作為其配置數據)。 StatelessWidget用于不需要維護狀態的場景,它通常在`build`方法中通過嵌套其它Widget來構建UI,在構建過程中會遞歸的構建其嵌套的Widget。我們看一個簡單的例子: ``` class Echo extends StatelessWidget { const Echo({ Key key, @required this.text, this.backgroundColor:Colors.grey, }):super(key:key); final String text; final Color backgroundColor; @override Widget build(BuildContext context) { return Center( child: Container( color: backgroundColor, child: Text(text), ), ); } } ``` 上面的代碼,實現了一個回顯字符串的`Echo` widget。 > 按照慣例,widget的構造函數應使用命名參數,命名參數中的必要參數要添加@required標注,這樣有利于靜態代碼分析器進行檢查,另外,在繼承widget時,第一個參數通常應該是`Key`,如果接受子widget的child參數,那么通常應該將它放在參數列表的最后。同樣是按照慣例,widget的屬性應被聲明為`final`,防止被意外改變。 然后我們可以通過如下方式使用它: ``` Widget build(BuildContext context) { return Echo(text: "hello world"); } ``` ![](https://box.kancloud.cn/587e85ad59135a506b1ce81ae9fe923f_200x356.png) ## Stateful Widget 和StatelessWidget一樣,StatefulWidget也是繼承自widget類,并重寫了`createElement()`方法,不同的是返回的`Element` 對象并不相同;另外StatefulWidget類中添加了一個新的接口`createState()`,下面我們看看StatefulWidget的類定義: ``` abstract class StatefulWidget extends Widget { const StatefulWidget({ Key key }) : super(key: key); @override StatefulElement createElement() => new StatefulElement(this); @protected State createState(); } ``` - `StatefulElement` 間接繼承自`Element`類,與StatefulWidget相對應(作為其配置數據)。`StatefulElement`中可能會多次調用`createState()`來創建狀態(State)對象。 - `createState()` 用于創建和Stateful widget相關的狀態,它在Stateful widget的生命周期中可能會被多次調用。例如,當一個Stateful widget同時插入到widget樹的多個位置時,Flutter framework就會調用該方法為每一個位置生成一個獨立的State實例,其實,本質上就是一個`StatefulElement`對應一個State實例。 > 在本書中經常會出現“樹“的概念,在不同的場景可能指不同的意思,在說“widget樹”時它可以指widget結構樹,但由于widget與Element有對應關系(一可能對多),在有些場景(Flutter的SDK文檔中)也代指“UI樹”的意思。而在stateful widget中,State對象也和`StatefulElement`具有對應關系(一對一),所以在Flutter的SDK文檔中,可以經常看到“從樹中移除State對象”或“插入State對象到樹中”這樣的描述。其實,無論哪種描述,其意思都是在描述“一棵構成用戶界面的節點元素的樹”,讀者不必糾結于這些概念,還是那句話“得其神,忘其形”,因此,本書中出現的各種“樹”,如果沒有特別說明,讀者都可抽象的認為它是“一棵構成用戶界面的節點元素的樹”。 ### State 一個StatefulWidget類會對應一個State類,State表示與其對應的StatefulWidget要維護的狀態,State中的保存的狀態信息可以: 1. 在widget build時可以被同步讀取。 2. 在widget生命周期中可以被改變,當State被改變時,可以手動調用其`setState()`方法通知Flutter framework狀態發生改變,Flutter framework在收到消息后,會重新調用其`build`方法重新構建widget樹,從而達到更新UI的目的。 State中有兩個常用屬性: 1. `widget`,它表示與該State實例關聯的widget實例,由Flutter framework動態設置。注意,這種關聯并非永久的,因為在應用聲明周期中,UI樹上的某一個節點的widget實例在重新構建時可能會變化,但State實例只會在第一次插入到樹中時被創建,當在重新構建時,如果widget被修改了,Flutter framework會動態設置State.widget為新的widget實例。 2. `context`,它是`BuildContext`類的一個實例,表示構建widget的上下文,它是操作widget在樹中位置的一個句柄,它包含了一些查找、遍歷當前Widget樹的一些方法。每一個widget都有一個自己的context對象。 > 對于`BuildContext`讀者現在可以先作了解,隨著本書后面內容的展開,也會用到Context的一些方法,讀者可以通過具體的場景對其有個直觀的認識。關于`BuildContext`更多的內容,我們也將在后面高級部分再深入介紹。 #### State生命周期 理解State的生命周期對flutter開發非常重要,為了加深讀者印象,本節我們通過一個實例來演示一下State的生命周期。在接下來的示例中,我們實現一個計數器widget,點擊它可以使計數器加1,由于要保存計數器的數值狀態,所以我們應繼承StatefulWidget,代碼如下: ``` class CounterWidget extends StatefulWidget { const CounterWidget({ Key key, this.initValue: 0 }); final int initValue; @override _CounterWidgetState createState() => new _CounterWidgetState(); } ``` `CounterWidget`接收一個`initValue`整形參數,它表示計數器的初始值。下面我們看一下State的代碼: ``` class _CounterWidgetState extends State<CounterWidget> { int _counter; @override void initState() { super.initState(); //初始化狀態 _counter=widget.initValue; print("initState"); } @override Widget build(BuildContext context) { print("build"); return Center( child: FlatButton( child: Text('$_counter'), //點擊后計數器自增 onPressed:()=>setState(()=> ++_counter) , ), ); } @override void didUpdateWidget(CounterWidget oldWidget) { super.didUpdateWidget(oldWidget); print("didUpdateWidget"); } @override void deactivate() { super.deactivate(); print("deactive"); } @override void dispose() { super.dispose(); print("dispose"); } @override void reassemble() { super.reassemble(); print("reassemble"); } @override void didChangeDependencies() { super.didChangeDependencies(); print("didChangeDependencies"); } } ``` 接下來,我們創建一個新路由,在新路由中,我們只顯示一個`CounterWidget`: ``` Widget build(BuildContext context) { return CounterWidget(); } ``` 我們運行應用并打開該路由頁面,在新路由頁打開后,屏幕中央就會出現一個數字0,然后控制臺日志輸出: ``` I/flutter ( 5436): initState I/flutter ( 5436): didChangeDependencies I/flutter ( 5436): build ``` 可以看到,在StatefulWidget插入到Widget樹時首先`initState`方法會被調用。 然后我們點擊??按鈕熱重載,控制臺輸出日志如下: ``` I/flutter ( 5436): reassemble I/flutter ( 5436): didUpdateWidget I/flutter ( 5436): build ``` 可以看到此時`initState` 和`didChangeDependencies`都沒有被調用,而此時`didUpdateWidget`被調用。 接下來,我們在widget樹中移除`CounterWidget`,將路由`build`方法改為: ``` Widget build(BuildContext context) { //移除計數器 //return CounterWidget(); //隨便返回一個Text() return Text("xxx"); } ``` 然后熱重載,日志如下: ``` I/flutter ( 5436): reassemble I/flutter ( 5436): deactive I/flutter ( 5436): dispose ``` 我們可以看到,在`CounterWidget`從widget樹中移除時,`deactive`和`dispose`會依次被調用。 下面我們來看看各個回調函數: - `initState`:當Widget第一次插入到Widget樹時會被調用,對于每一個State對象,Flutter framework只會調用一次該回調,所以,通常在該回調中做一些一次性的操作,如狀態初始化、訂閱子樹的事件通知等。不能在該回調中調用`BuildContext.inheritFromWidgetOfExactType`(該方法用于在Widget樹上獲取離當前widget最近的一個父級`InheritFromWidget`,關于`InheritedWidget`我們將在后面章節介紹),原因是在初始化完成后,Widget樹中的`InheritFromWidget`也可能會發生變化,所以正確的做法應該在在`build()`方法或`didChangeDependencies()`中調用它。 - `didChangeDependencies()`:當State對象的依賴發生變化時會被調用;例如:在之前`build()` 中包含了一個`InheritedWidget`,然后在之后的`build()` 中`InheritedWidget`發生了變化,那么此時`InheritedWidget`的子widget的`didChangeDependencies()`回調都會被調用。典型的場景是當系統語言Locale或應用主題改變時,Flutter framework會通知widget調用此回調。 - `build()`:此回調讀者現在應該已經相當熟悉了,它主要是用于構建Widget子樹的,會在如下場景被調用: 1. 在調用`initState()`之后。 2. 在調用`didUpdateWidget()`之后。 3. 在調用`setState()`之后。 4. 在調用`didChangeDependencies()`之后。 5. 在State對象從樹中一個位置移除后(會調用deactivate)又重新插入到樹的其它位置之后。 - `reassemble()`:此回調是專門為了開發調試而提供的,在熱重載(hot reload)時會被調用,此回調在Release模式下永遠不會被調用。 - `didUpdateWidget()`:在widget重新構建時,Flutter framework會調用`Widget.canUpdate`來檢測Widget樹中同一位置的新舊節點,然后決定是否需要更新,如果`Widget.canUpdate`返回`true`則會調用此回調。正如之前所述,`Widget.canUpdate`會在新舊widget的key和runtimeType同時相等時會返回true,也就是說在在新舊widget的key和runtimeType同時相等時`didUpdateWidget()`就會被調用。 - `deactivate()`:當State對象從樹中被移除時,會調用此回調。在一些場景下,Flutter framework會將State對象重新插到樹中,如包含此State對象的子樹在樹的一個位置移動到另一個位置時(可以通過GlobalKey來實現)。如果移除后沒有重新插入到樹中則緊接著會調用`dispose()`方法。 - `dispose()`:當State對象從樹中被永久移除時調用;通常在此回調中釋放資源。 > todo: 這里缺一張生命周期圖 注意:在繼承StatefulWidget重寫其方法時,對于包含@mustCallSuper標注的父類方法,都要在子類方法中先調用父類方法。 ## 狀態管理 響應式的編程框架中都會有一個永恒的主題——“狀態管理”,無論是在React/Vue(兩者都是支持響應式編程的web開發框架)還是Flutter,他們討論的問題和解決的思想都是一致的。所以,如果你對React/Vue的狀態管理有了解,可以跳過本節。言歸正傳,我們想一個問題,stateful widget的狀態應該被誰管理?widget本身?父widget?都會?還是另一個對象?答案是取決于實際情況!以下是管理狀態的最常見的方法: - Widget管理自己的state。 - 父widget管理子widget狀態。 - 混合管理(父widget和子widget都管理狀態)。 如何決定使用哪種管理方法?以下原則可以幫助你決定: - 如果狀態是用戶數據,如復選框的選中狀態、滑塊的位置,則該狀態最好由父widget管理。 - 如果狀態是有關界面外觀效果的,例如顏色、動畫,那么狀態最好由widget本身來管理。 - 如果某一個狀態是不同widget共享的則最好由它們共同的父widget管理。 在widget內部管理狀態封裝性會好一些,而在父widget中管理會比較靈活。有些時候,如果不確定到底該怎么管理狀態,那么推薦的首選是在父widget中管理(靈活會顯得更重要一些)。 接下來,我們將通過創建三個簡單示例TapboxA、TapboxB和TapboxC來說明管理狀態的不同方式。 這些例子功能是相似的 ——創建一個盒子,當點擊它時,盒子背景會在綠色與灰色之間切換。狀態 `_active`確定顏色:綠色為`true` ,灰色為`false`。 ![](https://box.kancloud.cn/82a5b546f2bd93fe7d6d87b013ad1bcb_200x200.png)![](https://box.kancloud.cn/4cea3cedefdebdf3be47e7856002a17a_200x200.png) 下面的例子將使用GestureDetector來識別點擊事件,關于該GestureDetector的詳細內容我們將在后面“事件處理”一章中介紹。 ### Widget管理自身狀態 \_TapboxAState 類: - 管理TapboxA的狀態。 - 定義`_active`:確定盒子的當前顏色的布爾值。 - 定義`_handleTap()`函數,該函數在點擊該盒子時更新`_active`,并調用`setState()`更新UI。 - 實現widget的所有交互式行為。 ``` // TapboxA 管理自身狀態. //------------------------- TapboxA ---------------------------------- class TapboxA extends StatefulWidget { TapboxA({Key key}) : super(key: key); @override _TapboxAState createState() => new _TapboxAState(); } class _TapboxAState extends State<TapboxA> { bool _active = false; void _handleTap() { setState(() { _active = !_active; }); } Widget build(BuildContext context) { return new GestureDetector( onTap: _handleTap, child: new Container( child: new Center( child: new Text( _active ? 'Active' : 'Inactive', style: new TextStyle(fontSize: 32.0, color: Colors.white), ), ), width: 200.0, height: 200.0, decoration: new BoxDecoration( color: _active ? Colors.lightGreen[700] : Colors.grey[600], ), ), ); } } ``` ### 父widget管理子widget的state 對于父widget來說,管理狀態并告訴其子widget何時更新通常是比較好的方式。 例如,IconButton是一個圖片按鈕,但它是一個無狀態的widget,因為我們認為父widget需要知道該按鈕是否被點擊來采取相應的處理。 在以下示例中,TapboxB通過回調將其狀態導出到其父項。由于TapboxB不管理任何狀態,因此它的父類為StatelessWidget。 ParentWidgetState 類: - 為TapboxB 管理`_active`狀態. - 實現`_handleTapboxChanged()`,當盒子被點擊時調用的方法. - 當狀態改變時,調用`setState()`更新UI. TapboxB 類: - 繼承`StatelessWidget`類,因為所有狀態都由其父widget處理。 - 當檢測到點擊時,它會通知父widget。 ``` // ParentWidget 為 TapboxB 管理狀態. //------------------------ ParentWidget -------------------------------- class ParentWidget extends StatefulWidget { @override _ParentWidgetState createState() => new _ParentWidgetState(); } class _ParentWidgetState extends State<ParentWidget> { bool _active = false; void _handleTapboxChanged(bool newValue) { setState(() { _active = newValue; }); } @override Widget build(BuildContext context) { return new Container( child: new TapboxB( active: _active, onChanged: _handleTapboxChanged, ), ); } } //------------------------- TapboxB ---------------------------------- class TapboxB extends StatelessWidget { TapboxB({Key key, this.active: false, @required this.onChanged}) : super(key: key); final bool active; final ValueChanged<bool> onChanged; void _handleTap() { onChanged(!active); } Widget build(BuildContext context) { return new GestureDetector( onTap: _handleTap, child: new Container( child: new Center( child: new Text( active ? 'Active' : 'Inactive', style: new TextStyle(fontSize: 32.0, color: Colors.white), ), ), width: 200.0, height: 200.0, decoration: new BoxDecoration( color: active ? Colors.lightGreen[700] : Colors.grey[600], ), ), ); } } ``` ### 混合管理 對于一些widget來說,混合管理的方式非常有用。在這種情況下,widget自身管理一些內部狀態,而父widget管理一些其他外部狀態。 在下面TapboxC示例中,按下時,盒子的周圍會出現一個深綠色的邊框。抬起時,邊框消失;點擊生效,盒子的顏色改變。 TapboxC將其`_active`狀態導出到其父widget中,但在內部管理其`_highlight`狀態。這個例子有兩個狀態對象`_ParentWidgetState`和`_TapboxCState`。 \_ParentWidgetStateC 對象: - 管理`_active` 狀態。 - 實現 `_handleTapboxChanged()` ,當盒子被點擊時調用。 - 當點擊盒子并且`_active`狀態改變時調用`setState()`更新UI。 \_TapboxCState 對象: - 管理`_highlight` state。 - `GestureDetector`監聽所有tap事件。當用戶點下時,它添加高亮(深綠色邊框);當用戶釋放時,會移除高亮。 - 當按下、抬起、或者取消點擊時更新`_highlight`狀態,調用`setState()`更新UI。 - 當點擊時,將狀態的改變傳遞給父widget. ``` //---------------------------- ParentWidget ---------------------------- class ParentWidgetC extends StatefulWidget { @override _ParentWidgetCState createState() => new _ParentWidgetCState(); } class _ParentWidgetCState extends State<ParentWidgetC> { bool _active = false; void _handleTapboxChanged(bool newValue) { setState(() { _active = newValue; }); } @override Widget build(BuildContext context) { return new Container( child: new TapboxC( active: _active, onChanged: _handleTapboxChanged, ), ); } } //----------------------------- TapboxC ------------------------------ class TapboxC extends StatefulWidget { TapboxC({Key key, this.active: false, @required this.onChanged}) : super(key: key); final bool active; final ValueChanged<bool> onChanged; _TapboxCState createState() => new _TapboxCState(); } class _TapboxCState extends State<TapboxC> { bool _highlight = false; void _handleTapDown(TapDownDetails details) { setState(() { _highlight = true; }); } void _handleTapUp(TapUpDetails details) { setState(() { _highlight = false; }); } void _handleTapCancel() { setState(() { _highlight = false; }); } void _handleTap() { widget.onChanged(!widget.active); } Widget build(BuildContext context) { // 在按下時添加綠色邊框,當抬起時,取消高亮 return new GestureDetector( onTapDown: _handleTapDown, // 處理按下事件 onTapUp: _handleTapUp, // 處理抬起事件 onTap: _handleTap, onTapCancel: _handleTapCancel, child: new Container( child: new Center( child: new Text(widget.active ? 'Active' : 'Inactive', style: new TextStyle(fontSize: 32.0, color: Colors.white)), ), width: 200.0, height: 200.0, decoration: new BoxDecoration( color: widget.active ? Colors.lightGreen[700] : Colors.grey[600], border: _highlight ? new Border.all( color: Colors.teal[700], width: 10.0, ) : null, ), ), ); } } ``` 另一種實現可能會將高亮狀態導出到父widget,同時保持`_active`狀態為內部,但如果你要將該TapBox給其它人使用,可能沒有什么意義。 開發人員只會關心該框是否處于Active狀態,而不在乎高亮顯示是如何管理的,所以應該讓TapBox內部處理這些細節。 ### 全局狀態管理 當應用中包括一些跨widget(甚至跨路由)的狀態需要同步時,上面介紹的方法很難勝任了。比如,我們有一個設置頁,里面可以設置應用語言,但是我們為了讓設置實時生效,我們期望在語言狀態發生改變時,我們的APP Widget能夠重新build一下,但我們的APP Widget和設置頁并不在一起。正確的做法是通過一個全局狀態管理器來處理這種“相距較遠”的widget之間的通信。目前主要有兩種辦法: 1. 實現一個全局的事件總線,將語言狀態改變對應為一個事件,然后在APP Widget所在的父widget`initState` 方法中訂閱語言改變的事件,當用戶在設置頁切換語言后,我們觸發語言改變事件,然后APP Widget那邊就會收到通知,然后重新`build`一下即可。 2. 使用redux這樣的全局狀態包,讀者可以在pub上查看其詳細信息。 本書后面**事件處理**一章中會實現一個全局事件總線。 ## Flutter widget庫介紹 Flutter提供了一套豐富、強大的基礎widget,在基礎widget庫之上Flutter又提供了一套Material風格(Android默認的視覺風格)和一套Cupertino風格(iOS視覺風格)的widget庫。要使用基礎widget庫,需要先導入: ``` import 'package:flutter/widgets.dart'; ``` 下面我們介紹一下常用的widget。 ### 基礎widget - [`Text`](https://docs.flutter.io/flutter/widgets/Text-class.html):該 widget 可讓您創建一個帶格式的文本。 - [`Row`](https://docs.flutter.io/flutter/widgets/Row-class.html)、 [`Column`](https://docs.flutter.io/flutter/widgets/Column-class.html): 這些具有彈性空間的布局類Widget可讓您在水平(Row)和垂直(Column)方向上創建靈活的布局。其設計是基于web開發中的Flexbox布局模型。 - [`Stack`](https://docs.flutter.io/flutter/widgets/Stack-class.html): 取代線性布局 (譯者語:和Android中的FrameLayout相似),[`Stack`](https://docs.flutter.io/flutter/widgets/Stack-class.html)允許子 widget 堆疊, 你可以使用 [`Positioned`](https://docs.flutter.io/flutter/widgets/Positioned-class.html) 來定位他們相對于`Stack`的上下左右四條邊的位置。Stacks是基于Web開發中的絕對定位(absolute positioning )布局模型設計的。 - [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html): [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html) 可讓您創建矩形視覺元素。container 可以裝飾一個[`BoxDecoration`](https://docs.flutter.io/flutter/painting/BoxDecoration-class.html), 如 background、一個邊框、或者一個陰影。 [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html) 也可以具有邊距(margins)、填充(padding)和應用于其大小的約束(constraints)。另外, [`Container`](https://docs.flutter.io/flutter/widgets/Container-class.html)可以使用矩陣在三維空間中對其進行變換。 ### Material widget Flutter提供了一套豐富的Material widget,可幫助您構建遵循Material Design的應用程序。Material應用程序以[`MaterialApp`](https://docs.flutter.io/flutter/material/MaterialApp-class.html) widget開始, 該widget在應用程序的根部創建了一些有用的widget,比如一個Theme,它配置了應用的主題。 是否使用[`MaterialApp`](https://docs.flutter.io/flutter/material/MaterialApp-class.html)完全是可選的,但是使用它是一個很好的做法。在之前的示例中,我們已經使用過多個Material widget了,如:`Scaffold`、`AppBar`、`FlatButton`等。要使用Material widget,需要先引入它: ``` import 'package:flutter/material.dart'; ``` ### Cupertino widget Flutter也提供了一套豐富的Cupertino風格的widget,盡管目前還沒有Material widget那么豐富,但也在不斷的完善中。值得一提的是在Material widget庫中,有一些widget可以根據實際運行平臺來切換表現風格,比如`MaterialPageRoute`,在路由切換時,如果是Android系統,它將會使用Android系統默認的頁面切換動畫(從底向上),如果是iOS系統時,它會使用iOS系統默認的頁面切換動畫(從右向左)。由于在前面的示例中還沒有Cupertino widget的示例,我們實現一個簡單的Cupertino頁面: ``` //導入cupertino widget庫 import 'package:flutter/cupertino.dart'; class CupertinoTestRoute extends StatelessWidget { @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text("Cupertino Demo"), ), child: Center( child: CupertinoButton( color: CupertinoColors.activeBlue, child: Text("Press"), onPressed: () {} ), ), ); } } ``` 下面是在iPhoneX上頁面效果截圖: ![](https://box.kancloud.cn/3b5b2dd334062fb5cb03751f3a853ef5_200x433.png) ### 總結 Flutter提供了豐富的widget,在實際的開發中你可以隨意使用它們,不要怕引入過多widget庫會讓你的應用安裝包變大,這不是web開發,dart在編譯時只會編譯你使用了的代碼。由于Material和Cupertino都是在基礎widget庫之上的,所以如果你的應用中引入了這兩者之一,則不需要再引入`flutter/widgets.dart`了,因為它們內部已經引入過了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看