Flutter入门(一)Flutter Widget框架概述
Flutter Widget框架概述
✅Flutter第一部分(UI)第一篇:初識Widget
✅Flutter跨平臺移動端開發丨Widget、Element、State、狀態管理
Flutter Widget采用現代響應式框架構建,這是從 React 中獲得的靈感,中心思想是用widget構建你的UI。
Widget描述了他們的視圖在給定其當前配置和狀態時應該看起來像什么。當widget的狀態發生變化時,widget會重新構建UI,Flutter會對比前后變化的不同, 以確定底層渲染樹從一個狀態轉換到下一個狀態所需的最小更改(譯者語:類似于React/Vue中虛擬DOM的diff算法)。
runApp函數接受給定的Widget并使其成為widget樹的根。
在編寫應用程序時,通常會創建新的widget,這些widget是無狀態的StatelessWidget或者是有狀態的StatefulWidget, 具體的選擇取決于您的widget是否需要管理一些狀態。widget的主要工作是實現一個build函數,用以構建自身。一個widget通常由一些較低級別widget組成。Flutter框架將依次構建這些widget,直到構建到最底層的子widget時,這些最低層的widget通常為RenderObject,它會計算并描述widget的幾何形狀。
一、Widget、Element、State
Flutter跨平臺移動端開發丨Widget、Element、State、狀態管理
1、widget
(1)概念
widget 的主要工作是通過實現build 函數來構建自身。一個 widget 通常由一些低級別的 widget 組成,flutter 框架依次的構建這些低級別的 widget,直到構建到最底層的子 widget 時,它會計算并描述 widget 的幾何形狀
(2)分類
StatelessWidget:無狀態,比如標題欄中的標題
StatefulWidget:有狀態,創建時需要指定一個 State ,在需要更新 UI時調用 setState(VoidCallbackfn),并在 VoidCallback 中改變一些些變量數值等,組件會重新 build 以達到數顯狀態/UI的效果。
管理狀態的常見方法:
widget 管理自己的 state
父 widget 管理子 widget 狀態
混合管理
決定狀態管理的原則:
有關用戶數據由父 widget 管理
有關界面效果由 widget 本身管理
狀態被不同 widget 共享,由他們共同的父 widget 管理
(3)Widget支持庫
flutter 提供了一套豐富、強大的基礎 widget ,在此基礎上還提供了Android 默認風格庫: Material 與 IOS 風格庫:Cupertino。
(4)基礎Widget
Text:文本
Row:水平布局,基于 web Flexbox 布局模型。
Column:垂直布局,基于 web Flexbox 布局模型。
Stack:取代線性布局,與 Android 中 FrameLayout相似,允許子 widget 堆疊,使用 positioned 定位它們相對于上下左右四條邊的位置。基于 web absolute positioning(絕對定位) 布局模型
Container:矩形元素,可以裝飾 BoxDecoration,如 background、邊框、陰影,它可以具有邊距(margins)、填充(padding)和應用與其大小的約束(constraints)。Container 可以使用矩陣在三維空間中對其進行變換
2、element
Element定義
An instantiation of aWidgetat a particular location in the tree.
Widgets describe how to configure a subtree but the same widget can be used to configure multiple subtrees simultaneously because widgets are immutable. AnElementrepresents the use of a widget to configure a specific location in the tree. Over time, the widget associated with a given element can change, for example, if the parent widget rebuilds and creates a new widget for this location.
Elements form a tree. Most elements have a unique child, but some widgets (e.g., subclasses ofRenderObjectElement) can have multiple children.
widget 中主要包含了組件的配置數據,但它并不代表最終繪制在屏幕上的顯示元素,真正代表屏幕上顯示元素的是 element,widget 是 element 的配置數據,一個 widget 可同時對應多個 element
3、state
每一個 StatefulWidget 類都會對應一個 State 類,State 表示與其對應的 StatefulWidget 要維護的狀態,保存的狀態信息可以在 build 時被獲取,同時,在 widget 生命周期中可以被改變,改變發生時,可以調用其 setState() 方法通知 framework 發生改變,framework 會重新調用 build 方法重構 widget 樹,最終完成更新 UI 的目的。
state 中包含兩個常用屬性:widget和context。widget 屬性表示當前正在關聯的 widget 實例,但關聯關系可能會在 widget 重構時發生變化(framework 會動態設置 widget 屬性為最新的widget 對象)。context 屬性是 buildContext 類的實例,表示構建 widget 的上下文,每個 widget 都有一個自己的 context 對象,它包含了查找、遍歷當前 widget 樹的方法。
State 的生命周期:
主要文章:State
在StatefulWidget調用createState之后,框架將新的狀態對象插入樹中,然后調用狀態對象的initState。 子類化State可以重寫initState,以完成僅需要執行一次的工作。 例如,您可以重寫initState以配置動畫或訂閱platform services。initState的實現中需要調用super.initState。
當一個狀態對象不再需要時,框架調用狀態對象的dispose。 您可以覆蓋該dispose方法來執行清理工作。例如,您可以覆蓋dispose取消定時器或取消訂閱platform services。dispose典型的實現是直接調用super.dispose。
initState:當前 widget 對象插入 widget樹中時調用
didChangeDependencies:當前 State 對象的依賴項發生變化時調用
build:繪制當前界面布局時調用
reassemble:使用熱重載時調用
didUpdateWidget:widget 配置內容有變動重構時調用
deactivate:當前 widget 對象從 widget 樹中移出時調用
dispose:當前 widget 對象從 widget 樹中永久刪除時調用
二、基礎 Widget
Flutter跨平臺移動端開發丨Widget、Element、State、狀態管理
主要文章:widget概述-布局模型
Flutter有一套豐富、強大的基礎widget,其中以下是很常用的:
Text:該 widget 可讓創建一個帶格式的文本。
Row、Column: 這些具有彈性空間的布局類Widget可讓您在水平(Row)和垂直(Column)方向上創建靈活的布局。其設計是基于web開發中的Flexbox布局模型。
Stack: 取代線性布局 (譯者語:和Android中的LinearLayout相似),Stack允許子 widget 堆疊, 你可以使用Positioned來定位他們相對于Stack的上下左右四條邊的位置。Stacks是基于Web開發中的絕度定位(absolute positioning )布局模型設計的。
Container:Container可讓您創建矩形視覺元素。container 可以裝飾為一個BoxDecoration, 如 background、一個邊框、或者一個陰影。Container也可以具有邊距(margins)、填充(padding)和應用于其大小的約束(constraints)。另外,Container可以使用矩陣在三維空間中對其進行變換。
三、Material組件
主要文章:Widgets 總覽 - Material 組件
Flutter提供了許多widgets,可幫助您構建遵循Material Design的應用程序。Material應用程序以MaterialAppwidget開始, 該widget在應用程序的根部創建了一些有用的widget,其中包括一個Navigator, 它管理由字符串標識的Widget棧(即頁面路由棧)。Navigator可以讓您的應用程序在頁面之間的平滑的過渡。 是否使用MaterialApp完全是可選的,但是使用它是一個很好的做法。
四、處理手勢
class MyButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
      onTap: () {
        print('MyButton was tapped!');
      },
      child: new Container(
        height: 36.0,
        padding: const EdgeInsets.all(8.0),
        margin: const EdgeInsets.symmetric(horizontal: 8.0),
        decoration: new BoxDecoration(
          borderRadius: new BorderRadius.circular(5.0),
          color: Colors.lightGreen[500],
        ),
        child: new Center(
          child: new Text('Engage'),
        ),
      ),
    );
  }
}
該GestureDetectorwidget并不具有顯示效果,而是檢測由用戶做出的手勢。 當用戶點擊Container時,GestureDetector會調用它的onTap回調, 在回調中,將消息打印到控制臺。您可以使用GestureDetector來檢測各種輸入手勢,包括點擊、拖動和縮放。
許多widget都會使用一個GestureDetector為其他widget提供可選的回調。 例如,IconButton、RaisedButton、 和FloatingActionButton,它們都有一個onPressed回調,它會在用戶點擊該widget時被觸發。
五、StatefulWidgetStatefulWidget State
Flutter跨平臺移動端開發丨Widget、Element、State、狀態管理
1、簡述
在Flutter中,這兩種類型的對象具有不同的生命周期: Widget是臨時對象,用于構建當前狀態下的應用程序,而State對象在多次調用build()之間保持不變,允許它們記住信息(狀態)。
2、關于狀態的demo,仔細體會:
(1)兩個無狀態的Widget 和 一個有狀態的Widget的清晰的分離了,顯示和更改的邏輯,將復雜性邏輯封裝在各個widget中,同時保持父項的簡單性。
(2)事件流是“向上”傳遞的,而狀態流是“向下”傳遞的
ps:函數的生命和調用分離,實現了回調;體會無狀態,和有狀態部件的區別。
class CounterDisplay extends StatelessWidget {
  CounterDisplay({this.count});
  final int count;
  @override
  Widget build(BuildContext context) {
    return new Text('Count: $count');
  }
}
class CounterIncrementor extends StatelessWidget {
  CounterIncrementor({this.onPressed});
  final VoidCallback onPressed;
  @override
  Widget build(BuildContext context) {
    return new RaisedButton(
      onPressed: onPressed,
      child: new Text('Increment'),
    );
  }
}
class Counter extends StatefulWidget {
  @override
  _CounterState createState() => new _CounterState();
}
class _CounterState extends State<Counter> {
  int _counter = 0;
  void _increment() {
    setState(() {
      ++_counter;
    });
  }
  @override
  Widget build(BuildContext context) {
    return new Row(children: <Widget>[
      new CounterIncrementor(onPressed: _increment),
      new CounterDisplay(count: _counter),
    ]);
  }
}
3、Key
深入淺出 Key
Flutter中key的作用 demo
(1)什么是key
我們知道 Widget 可以有Stateful和Stateless兩種。Key 能夠幫助開發者在Widget tree 中保存狀態
AKeyis an identifier forWidgets,Elements andSemanticsNodes.
A new widget will only be used to update an existing element if its key is the same as the key of the current widget associated with the element.
Keys must be unique amongst theElements with the same parent.
Subclasses ofKeyshould either subclassLocalKeyorGlobalKey.
See also the discussion atWidget.key.
Widget的源碼:
@immutable
abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });
  final Key key;
  ···
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}
我們知道 Widget 只是一個配置且無法修改,而 Element 才是真正被使用的對象,并可以修改。當新的 Widget 到來時將會調用 canUpdate 方法,來確定這個 Element是否需要更新。
canUpdate對兩個(新老) Widget 的runtimeType和key進行比較,從而判斷出當前的Element 是否需要更新。
StatelessContainer比較過程:
在 StatelessContainer 中,我們并沒有傳入 key ,所以只比較它們的 runtimeType。我們將 color 屬性定義在了 Widget 中,這將導致他們具有不同的 runtimeType。所以在 StatelessContainer 這個例子中,Flutter能夠正確的交換它們的位置。
StatefulContainer 比較過程:
而在 StatefulContainer 的例子中,我們將 color 的定義放在了 State 中,Widget 并不保存 State,真正 hold State 的引用的是 Stateful Element。當我們沒有給 Widget 任何 key 的時候,將會只比較這兩個 Widget 的 runtimeType 。由于兩個 Widget 的屬性和方法都相同,canUpdate 方法將會返回 false,在 Flutter 看來,并沒有發生變化。所以這兩個 Element 將不會交換位置。而我們給 Widget 一個 key 之后,canUpdate 方法將會比較兩個 Widget 的 runtimeType 以及 key。并返回 true,現在 Flutter 就可以正確的感知到兩個 Widget 交換了順序了。 (這里 runtimeType 相同,key 不同)
比較范圍:
···
class _ScreenState extends State<Screen> {
  List<Widget> widgets = [
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: StatefulContainer(key: UniqueKey(),),
    ),
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: StatefulContainer(key: UniqueKey(),),
    ),
  ];
···
分析;
我們分析一下這次的Widget Tree 和 Element Tree,當我們交換元素后,Flutter element-to-widget matching algorithm,(元素-組件匹配算法),開始進行對比,算法每次只對比一層,即Padding這一層。顯然,Padding并沒有發生本質的變化。
于是開始進行第二層對比,在對比時Flutter發現元素與組件的Key并不匹配,于是,把它設置成不可用狀態,但是這里所使用的Key只是本地Key(Local Key),Flutter并不能找到另一層里面的Key(即另外一個Padding Widget中的Key)所以,Flutter就創建了一個新的Widget,而這個Widget的顏色就成了我們看到的『隨機色』。
(2)什么時候使用key?
當使用Stateless Widget時,我們并不需要使用key,當使用Stateful Widget時,集合內有數據移動和改變并且需要展示到界面時才需要key。
(3)Key應該用到哪?
我們的Key要設置到組件樹的頂層,而這一層在改變時,才能復用或者更新狀態。
(4)用哪一種Key?
Flutter中有很多Key,但是總體分為兩種 Local Key和Global Key兩種。
對于Key的研究還不是特別多,有時間再補一篇,上面的例子,因為沒有數據,所以使用了UniqueKey,在真實的開發中,我們可以用Model中的id作為ObjectKey。
GlobalKey其實是對應于LocalKey,上面我們說Padding中的就是LocalKey,Global即可以在多個頁面或者層級復用,比如兩個頁面也可也同時保持一個狀態。
總結
以上是生活随笔為你收集整理的Flutter入门(一)Flutter Widget框架概述的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 蓝牙耳机怎么使用电脑耳机如何使用
 - 下一篇: 手机怎么共享wifi/数据网络给电脑使用