手寫一個(gè)Flutter State Widget,來讓你徹底理解State的來龍去脈

image

往期相關(guān)內(nèi)容

前言

在上期官疲,我們手寫了一個(gè)Widget的實(shí)現(xiàn)泳叠,并接觸到了Element耀怜,經(jīng)過一系列的分析,我們對(duì)Widget和Element的認(rèn)識(shí)更進(jìn)一步桐愉,那么這期我們就來深入理解下State财破,相信大家在開發(fā)過程中,總會(huì)用到StatefulWidget从诲,那么官方為什么設(shè)計(jì)一個(gè)含有State的Widget左痢?State生命周期是怎么來的?為什么State可以更新UI系洛?帶著一些疑問俊性,我們不直接分析源碼,而是手寫一個(gè)帶有State的Widget怎么樣描扯?我們來做一個(gè)帶State的Widget定页,讓它有生命周期和更新UI的能力。

本次主要內(nèi)容

  • 宏觀看state是什么绽诚,微觀看State
  • State類繼承關(guān)系圖
  • 手寫一個(gè)帶State的Widget

宏觀看state是什么典徊,微觀看State

image

從宏觀來看,flutter的UI是聲明式的恩够,那為什么是聲明式卒落?這就要從Win32到Web再到Android和Ios說起,他們都是命令式的編程風(fēng)格蜂桶,如下:

//android
TextView tv = TextView()
tv.setText("text")

當(dāng)UI發(fā)生變化的時(shí)候儡毕,你必須調(diào)用setText來實(shí)現(xiàn),但flutter恰相反屎飘,它為了減輕開發(fā)人員的負(fù)擔(dān)妥曲,讓開發(fā)人員只關(guān)心當(dāng)前應(yīng)用的狀態(tài),并交給框架自動(dòng)將狀態(tài)通過函數(shù)渲染在UI上钦购,那么這樣做有什么好處呢檐盟?

  • 開發(fā)人員只關(guān)心狀態(tài)的變化,從架構(gòu)上做到了UI和數(shù)據(jù)的分離
  • 更深入的講押桃,其實(shí)flutter 真實(shí)的UI對(duì)象是RenderObjects葵萎,Widget是不變的,每次刷新UI都會(huì)構(gòu)建新的子Widget樹唱凯,并通過Element過濾羡忘,最終RenderObject只是很小的改動(dòng),提高了渲染的效率磕昼。

那么有什么缺點(diǎn)嗎卷雕?

  • 不合理的狀態(tài)管理,導(dǎo)致整個(gè)頁面的頻繁build
  • 在Widget樹中加入了狀態(tài)的計(jì)算票从,會(huì)導(dǎo)致狀態(tài)管理的混亂漫雕,不統(tǒng)一

最理想的就是如上圖的公式:UI= f(state) 舉個(gè)例子:

class TestState extends StatefulWidget {
  @override
  _TestState createState() => _TestState();
}

class _TestState extends State<TestState> {
  FunState _funState = FunState();

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: [
          Text(_funState.state + "state"), /// 不推薦
          Text(_funState.getState()) /// 推薦寫法滨嘱,f(state) 
        ],
      ),
    );
  }
}

class FunState {
  String state = "state";
  getState() {
    return state + "Test";
  }
}

看到了吧,我們不推薦你這樣哦

Text(_funState.state + "state"), /// 不推薦

這就是我要說的宏觀state浸间,我們簡(jiǎn)單做個(gè)定義:state其實(shí)就是反應(yīng)出當(dāng)前UI的狀態(tài)太雨。那么微觀呢?其實(shí)就是StatefulWidget的State魁蒜,都知道每個(gè)StatefulWidget會(huì)對(duì)應(yīng)一個(gè)State囊扳,上期我們也學(xué)習(xí)了Widget,了解到Widget實(shí)際上是通過Element來展示UI的兜看,那么State到底是什么角色锥咸,有什么作用呢?或者說铣减,為什么google要這么設(shè)計(jì)呢她君?讓我們來慢慢揭曉答案,并最終總結(jié)一下葫哗。

State類繼承關(guān)系圖

image

像我常用的Form,F(xiàn)ormField,Overlay,Scaffold Widget球涛,它們都會(huì)對(duì)應(yīng)一個(gè)自己的State劣针,當(dāng)然也有更深一層的繼承關(guān)系如AnimatedWidgetBaseState,但它的子類都是私有的亿扁。以及其他的State捺典,通過類的繼承關(guān)系,大致了解到从祝,State類不需要特別深入的繼承關(guān)系襟己,比Widget和Element都稍微簡(jiǎn)單一些,flutter在設(shè)計(jì)之初牍陌,就一致貫穿一個(gè)設(shè)計(jì)思想就是組合大于繼承擎浴,所以這也是整個(gè)UI框架的特點(diǎn),也是類圖都很簡(jiǎn)單的主要原因毒涧。

手寫一個(gè)帶State的Widget

我們還是上期的套路贮预,繼承最底層的Widget來實(shí)現(xiàn),這次加一個(gè)State契讲,來偽裝成StatefulWidget仿吞,來吧。

class StateWidget extends Widget{
  /// 構(gòu)造函數(shù)
  const StateWidget({ Key key }) : super(key: key);
  
  @override
  Element createElement() {
    // TODO: implement createElement
    throw UnimplementedError();
  }
}

創(chuàng)建StateWidget類捡偏,繼承自Widget唤冈,它讓我們實(shí)現(xiàn)一個(gè)Element,那我們就再創(chuàng)建一個(gè)Element,這次我們用ComponentElement银伟,上期我們使用過Element了你虹,實(shí)現(xiàn)起來較麻煩绘搞,這期我們要了解的是State對(duì)吧,所以我們繼承ComponentElement來快速的實(shí)現(xiàn)并理解State

class StateWidget extends Widget{
  /// 構(gòu)造函數(shù)
  const StateWidget({ Key key }) : super(key: key);

  @override
  Element createElement() {
    return StateElement(this);
  }
}

class StateElement extends ComponentElement{
  
  StateElement(Widget widget) : super(widget);

  @override
  Widget build() {
    
  }
}

我們印象中State有很多屬性和函數(shù)售葡,都有那些呢看杭?請(qǐng)看圖

image

那我們就模仿一下,把這些函數(shù)定義一下挟伙,代碼如下

abstract class States<T extends StateWidget> {

  T get widget => _widget;
  T _widget;

  BuildContext get context => _element;
  StateElement _element;
  
  @protected
  @mustCallSuper
  void initState() {}

  @protected
  @mustCallSuper
  void didUpdateWidget(covariant T oldWidget) {}

  @protected
  @mustCallSuper
  void reassemble() {}

  @protected
  @mustCallSuper
  void setState(VoidCallback fn) {}

  @protected
  @mustCallSuper
  void deactivate() {}

  @protected
  @mustCallSuper
  void dispose() {}

  @protected
  Widget build(BuildContext context);

  @protected
  @mustCallSuper
  void didChangeDependencies() { }

}

好了我們的States就這樣被定義完了楼雹,protected關(guān)鍵字跟java的作用域應(yīng)該是一樣的,mustCallSuper是讓子類實(shí)現(xiàn)必須調(diào)用super.當(dāng)前函數(shù)尖阔,當(dāng)然它還有debugFillProperties等debug相關(guān)的函數(shù)贮缅,這些我們先不關(guān)心,我們先把最核心的問題搞定介却,接下來谴供,如何將States接入Widget呢?想想我們之前怎么用的齿坷?

  @override
  _TestState createState() => _TestState();

對(duì)的桂肌,Widget有個(gè)createState函數(shù),我們也來加一下永淌,如下:

abstract class StateWidget extends Widget {
  /// 構(gòu)造函數(shù)
  const StateWidget({Key key}) : super(key: key);

  @override
  Element createElement() {
    return StateElement(this);
  }

  @protected
  @factory
  States createState();
}

factory的注釋含義:
用于注釋實(shí)例或靜態(tài)方法崎场。 表示該方法要么是抽象的,要么必須返回新分配的對(duì)象或“null”遂蛀。另外谭跨,每個(gè)實(shí)現(xiàn)或覆蓋該方法都是隱式的使用相同的注釋進(jìn)行注釋。

通過實(shí)現(xiàn)李滴,我們發(fā)現(xiàn)State其實(shí)同時(shí)有Widget和Element的引用的螃宙,Widget已經(jīng)完成了,那再來看看Element如何做呢所坯?我們?cè)賮砜聪翬lement的代碼

class StateElement extends ComponentElement {

  ///這里將之前的Widget改為StateWidget谆扎,免得強(qiáng)轉(zhuǎn)
  StateElement(StateWidget widget) : super(widget);

  @override
  Widget build() {

  }
}

繼承自ComponentElement,覆蓋build函數(shù)包竹,而State里面恰巧有個(gè)抽象函數(shù)build燕酷,那么肯定是這里了

class StateElement extends ComponentElement {

  States<StateWidget> get state => _state;
  States<StateWidget> _state;

  StateElement(StateWidget widget) : super(widget);

  @override
  Widget build() {
    return _state.build(this);
  }
}

在Element里緩存一下State,并在build中調(diào)用_state.build(this), 這個(gè)this就是我們熟悉的BuildContext周瞎,而BuildContext的實(shí)例就是當(dāng)前Element對(duì)象∶缢酰現(xiàn)在你會(huì)發(fā)現(xiàn),_state并沒有賦值對(duì)吧声诸,它是widget里的createState函數(shù)返回的酱讶,那我們什么時(shí)候調(diào)用合適呢?為了避免它多次createState彼乌,在構(gòu)造函數(shù)里是不是更合適呢泻肯?放進(jìn)去如下

class StateElement extends ComponentElement {

  States<StateWidget> get state => _state;
  States<StateWidget> _state;

  StateElement(StateWidget widget)
        ///創(chuàng)建State
      : _state = widget.createState(),
        super(widget){
    ///斷言判斷
    assert(_state._element == null);
    ///給State里的element賦值渊迁,也就是你在State里獲取的context
    _state._element = this;
    assert(_state._widget == null);
    ///state里的widget賦值
    _state._widget = widget;
  }

  @override
  Widget build() {
    return _state.build(this);
  }
}

在構(gòu)造函數(shù)里已經(jīng)將State里的element,widget統(tǒng)統(tǒng)賦值了灶挟,緊接著就是State的initState()函數(shù)琉朽,這是我們經(jīng)常用的初始化函數(shù),那么它是在Element什么時(shí)候被調(diào)用的呢稚铣?或者說什么時(shí)候調(diào)用比較合適箱叁,首先一點(diǎn),它肯定只調(diào)用一次惕医,不可能初始化兩次把耕漱,這樣不太合理,有人說放構(gòu)造里行嗎抬伺?我們?cè)賮砜纯碋lement的生命周期

image

系統(tǒng)調(diào)用createElement后螟够,當(dāng)Element真正被掛載到樹中的時(shí)候,才會(huì)調(diào)用mount峡钓,如果Element根本沒有掛載到UI上妓笙,我們是不是就沒必要初始化呢?那放在構(gòu)造合適嗎能岩?不合適對(duì)吧给郊,所以實(shí)現(xiàn)如下:

class StateElement extends ComponentElement {

  States<StateWidget> get state => _state;
  States<StateWidget> _state;
  bool isNeedInit;

  StateElement(StateWidget widget)
        ///創(chuàng)建State
      : _state = widget.createState(),
        super(widget){
    ///斷言判斷
    assert(_state._element == null);
    ///給State里的element賦值,也就是你在State里獲取的context
    _state._element = this;
    assert(_state._widget == null);
    ///state里的widget賦值
    _state._widget = widget;
  }

  @override
  void mount(Element parent, newSlot) {
    super.mount(parent, newSlot);
    assert(isNeedInit == null);
    _state.initState();
    isNeedInit = false;
  }

  @override
  Widget build() {
    return _state.build(this);
  }
}

在mount函數(shù)中調(diào)用initState函數(shù)捧灰,并通過isNeedInit變量控制只調(diào)用一次。緊接著看didChangeDependencies函數(shù)统锤,為什么說它呢毛俏?當(dāng)此State對(duì)象的依賴項(xiàng)更改時(shí)調(diào)用,還有就是在initState后調(diào)用饲窿,官方解釋:子類很少重寫此方法煌寇,因?yàn)榭蚣芸偸窃谝蕾図?xiàng)更改后調(diào)用build。一些子類確實(shí)重寫了此方法逾雄,因?yàn)楫?dāng)它們的依存關(guān)系發(fā)生變化時(shí)阀溶,它們需要做一些昂貴的工作(例如,網(wǎng)絡(luò)獲妊挥尽)银锻,并且對(duì)于每個(gè)構(gòu)建而言,所做的工作都太昂貴了做鹰。

詳細(xì)理解請(qǐng)看大佬分析:http://www.reibang.com/p/9cb6c57b796c

其實(shí)說白了就是Widget類型發(fā)生變化時(shí)击纬,就會(huì)觸發(fā),State里的didChangeDependencies觸發(fā)需要滿足兩個(gè)條件钾麸,一個(gè)就是第一次加載更振,它認(rèn)為Widget類型從null轉(zhuǎn)換為具體的Widget炕桨,再一個(gè)就是Element的Widget確實(shí)有了變化,系統(tǒng)調(diào)用Element的didChangeDependencies肯腕,這個(gè)時(shí)候才有必要執(zhí)行State的didChangeDependencies献宫,代碼實(shí)現(xiàn)如下:

class StateElement extends ComponentElement {

  States<StateWidget> get state => _state;
  States<StateWidget> _state;
  bool isNeedInit;

  bool _didChangeDependencies = false;


  StateElement(StateWidget widget)
        ///創(chuàng)建State
      : _state = widget.createState(),
        super(widget){
    ///斷言判斷
    assert(_state._element == null);
    ///給State里的element賦值,也就是你在State里獲取的context
    _state._element = this;
    assert(_state._widget == null);
    ///state里的widget賦值
    _state._widget = widget;
  }

  @override
  void mount(Element parent, newSlot) {
    super.mount(parent, newSlot);
    assert(isNeedInit == null);
    _state.initState();
    ///第一次加載的時(shí)候实撒,Widget從Null變?yōu)榫唧w的Widget
    _state.didChangeDependencies();
    isNeedInit = false;
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    ///Element的Widget確實(shí)有了變化
    _didChangeDependencies = true;
  }
  
  @override
  void performRebuild() {
    ///這個(gè)時(shí)候才有必要執(zhí)行State的didChangeDependencies
    if (_didChangeDependencies) {
      _state.didChangeDependencies();
      _didChangeDependencies = false;
    }
    super.performRebuild();
  }

  @override
  Widget build() {
    return _state.build(this);
  }
}

在element的performRebuild函數(shù)中執(zhí)行State的didChangeDependencies姊途,條件就是在系統(tǒng)執(zhí)行了Element的didChangeDependencies函數(shù)。

再往下奈惑,我們看下reassemble函數(shù)吭净,這個(gè)函數(shù)是干嘛的呢?此回調(diào)是專門為了開發(fā)調(diào)試而提供的肴甸,在熱重載(hot reload)時(shí)會(huì)被調(diào)用寂殉,此回調(diào)在Release模式下永遠(yuǎn)不會(huì)被調(diào)用。這個(gè)我們不必關(guān)心太多原在,直接在Element里調(diào)用即可友扰,實(shí)現(xiàn)如下:

  @override
  void reassemble() {
    _state.reassemble();
    super.reassemble();
  }

再來看下didUpdateWidget函數(shù),在widget重新構(gòu)建時(shí)庶柿,F(xiàn)lutter framework會(huì)調(diào)用Widget.canUpdate來檢測(cè)Widget樹中同一位置的新舊節(jié)點(diǎn)村怪,然后決定是否需要更新,如果Widget.canUpdate返回true則會(huì)調(diào)用此回調(diào)浮庐。正如之前所述甚负,Widget.canUpdate會(huì)在新舊widget的key和runtimeType同時(shí)相等時(shí)會(huì)返回true,也就是說在在新舊widget的key和runtimeType同時(shí)相等時(shí)didUpdateWidget()就會(huì)被調(diào)用审残,但你發(fā)現(xiàn)Element里面沒有這個(gè)函數(shù)梭域,它只有update函數(shù)。然后我看了下StatefulElement的實(shí)現(xiàn)就是在這里調(diào)用的didUpdateWidget搅轿,那我們來實(shí)現(xiàn)下病涨,看看都什么邏輯

class StateElement extends ComponentElement {
  States<StateWidget> get state => _state;
  States<StateWidget> _state;
  bool isNeedInit;

  bool _didChangeDependencies = false;

  StateElement(StateWidget widget)

      ///創(chuàng)建State
      : _state = widget.createState(),
        super(widget) {
    ///斷言判斷
    assert(_state._element == null);

    ///給State里的element賦值,也就是你在State里獲取的context
    _state._element = this;
    assert(_state._widget == null);

    ///state里的widget賦值
    _state._widget = widget;
  }

  @override
  void mount(Element parent, newSlot) {
    super.mount(parent, newSlot);
    assert(isNeedInit == null);
    _state.initState();

    ///第一次加載的時(shí)候璧坟,Widget從Null變?yōu)榫唧w的Widget
    _state.didChangeDependencies();
    isNeedInit = false;
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    ///Element的Widget確實(shí)有了變化
    _didChangeDependencies = true;
  }

  @override
  void performRebuild() {
    ///這個(gè)時(shí)候才有必要執(zhí)行State的didChangeDependencies
    if (_didChangeDependencies) {
      _state.didChangeDependencies();
      _didChangeDependencies = false;
    }
    super.performRebuild();
  }

  @override
  void reassemble() {
    _state.reassemble();
    super.reassemble();
  }

  @override
  void update(Widget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);

    /// 先拿到舊的widget
    final StateWidget oldWidget = _state._widget;

    /// 強(qiáng)制將狀態(tài)至為可更新
    markNeedsBuild();

    /// 將State的Widget更新為新的Widget
    _state._widget = widget as StateWidget;
    /// 回調(diào)_state.didUpdateWidget
    _state.didUpdateWidget(oldWidget);
    /// 調(diào)用rebuild函數(shù)既穆,最終調(diào)用performRebuild,更新Element
    rebuild();
  }

  @override
  Widget build() {
    return _state.build(this);
  }
}

update函數(shù)如上實(shí)現(xiàn)雀鹃,你會(huì)發(fā)現(xiàn)ditUpdateWidget參數(shù)是舊的Widget幻工,這點(diǎn)使用的時(shí)候要注意哦。update函數(shù)結(jié)束了褐澎,來看下deactivate会钝,當(dāng)State對(duì)象從樹中被移除時(shí),會(huì)調(diào)用此回調(diào)。在一些場(chǎng)景下迁酸,F(xiàn)lutter framework會(huì)將State對(duì)象重新插到樹中先鱼,如包含此State對(duì)象的子樹在樹的一個(gè)位置移動(dòng)到另一個(gè)位置時(shí)(可以通過GlobalKey來實(shí)現(xiàn))。如果移除后沒有重新插入到樹中則緊接著會(huì)調(diào)用dispose()方法奸鬓”号希看實(shí)現(xiàn)很簡(jiǎn)單:

  @override
  void deactivate() {
    /// 不需要特殊處理
    _state.deactivate();
    super.deactivate();
  }

再來看下dispose函數(shù),dispose意義上是釋放串远,那Element是什么時(shí)候釋放的呢宏多?那肯定是unmount函數(shù)了,所以不言而喻澡罚,實(shí)現(xiàn)如下:

  @override
  void unmount() {
    super.unmount();
    _state.dispose();
    /// 至null來釋放調(diào)引用
    _state._element = null;
    _state = null;
  }

壓軸的函數(shù)setState來了伸但,幾乎State所有的生命周期函數(shù)里,沒有幾個(gè)是有實(shí)現(xiàn)的留搔,而setState需要實(shí)現(xiàn)更胖,它是Widget能夠重建的核心,直接上代碼分析哈:

abstract class States<T extends StateWidget> {
  T get widget => _widget;
  T _widget;

  BuildContext get context => _element;
  StateElement _element;

  @protected
  @mustCallSuper
  void initState() {}

  @protected
  @mustCallSuper
  void didUpdateWidget(covariant T oldWidget) {}

  @protected
  @mustCallSuper
  void reassemble() {}

  @protected
  @mustCallSuper
  void setState(VoidCallback fn) {
    assert(fn != null);
    ///...省略了狀態(tài)判斷
    final dynamic result = fn() as dynamic;
    assert(() {
      if (result is Future) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('setState() callback argument returned a Future.'),
          ErrorDescription(
              'The setState() method on $this was called with a closure or method that '
                  'returned a Future. Maybe it is marked as "async".'
          ),
          ErrorHint(
              'Instead of performing asynchronous work inside a call to setState(), first '
                  'execute the work (without updating the widget state), and then synchronously '
                  'update the state inside a call to setState().'
          ),
        ]);
      }
      // We ignore other types of return values so that you can do things like:
      //   setState(() => x = 3);
      return true;
    }());
    /// 最重要的一句隔显,markNeedsBuild却妨,讓Element處與可更新狀態(tài),等下framework層主動(dòng)刷新括眠。
    _element.markNeedsBuild();
  }

  @protected
  @mustCallSuper
  void deactivate() {}

  @protected
  @mustCallSuper
  void dispose() {}

  @protected
  Widget build(BuildContext context);

  @protected
  @mustCallSuper
  void didChangeDependencies() {}
}

它首先判斷了fn是否為空彪标,然后加入了state狀態(tài)的判斷,這里我省略了掷豺,想看的可以直接看State源碼哦捞烟。往下就是對(duì)fn函數(shù)的Future情況處理,最最后調(diào)用了_element.markNeedsBuild();對(duì)哦当船,這其實(shí)才是我們Widget重新構(gòu)建的關(guān)鍵坷襟,它其實(shí)就是改了一個(gè)標(biāo)志位_dirty,設(shè)置true后生年,framework層就知道它要更新,會(huì)執(zhí)行響應(yīng)的更新廓奕,由于實(shí)際的更新是異步的抱婉,所以你可以在setState函數(shù)的前后或者函數(shù)中,都可以更新狀態(tài)桌粉。

好了終于實(shí)現(xiàn)完了蒸绩,是騾子是馬,總要拉出來溜溜铃肯,我們自己實(shí)現(xiàn)的States能用嗎患亿?來實(shí)驗(yàn)一發(fā)。代碼如下:

class TestStateWidget extends StateWidget {
  @override
  States<StateWidget> createState() {
    return TestStates();
  }
}

class TestStates extends States<TestStateWidget> {

  String data;
  int num = 0;

  @override
  void initState() {
    data = "123";
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(8),
      child: Column(
        children: [
          Text(data),
          MaterialButton(
            onPressed: () {
              setState(() {
                data = "456${num++}";
              });
            },
            child: Text("更新"),
          )
        ],
      ),
    );
  }
}

放入main.dart中:

 @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          // Center(
          //   // Center is a layout widget. It takes a single child and positions it
          //   // in the middle of the parent.
          //   child: isShow ? const TestWidget() : Container(),
          // ),
          // MaterialButton(
          //   onPressed: () {
          //     showialog(context);
          //   },
          //   child: Text('showDialog'),
          // ),
          // Center(
          //   // Center is a layout widget. It takes a single child and positions it
          //   // in the middle of the parent.
          //   child: isShow
          //       ? TestWidget(
          //           key: key,
          //         )
          //       : Container(
          //           padding: EdgeInsets.all(9),
          //           child: TestWidget(
          //             key: key,
          //           ),
          //         ),
          // ),
          TestStateWidget()
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            isShow = !isShow;
          });
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

運(yùn)行效果:

image

點(diǎn)擊更新

image

再點(diǎn)擊

image

沒毛病把,完成了步藕,效果很好惦界,實(shí)現(xiàn)了一套帶State的Widget對(duì)吧。下面我們來個(gè)總結(jié)咙冗。

總結(jié)

生命周期總結(jié)一張圖

image
  • State的狀態(tài)都是來源于Element沾歪,說白了就是Element的中介,或者叫委托更合適
  • Context可以是Element雾消,但State不能灾搏,因?yàn)樗麄儾皇抢^承關(guān)系。
  • 掌握Element的重要性又出來了立润,因?yàn)镾tate的功能受限于Element狂窑。
  • 在MVVM架構(gòu)中,State是不是充當(dāng)著VM的角色桑腮?我覺得可以這么認(rèn)為泉哈。

等等把,好了到旦,講到這里該告一段落了旨巷,期待你的認(rèn)可,點(diǎn)個(gè)贊就行添忘。感謝采呐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搁骑,隨后出現(xiàn)的幾起案子斧吐,更是在濱河造成了極大的恐慌,老刑警劉巖仲器,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件煤率,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡乏冀,警方通過查閱死者的電腦和手機(jī)蝶糯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辆沦,“玉大人昼捍,你說我怎么就攤上這事≈叮” “怎么了妒茬?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蔚晨。 經(jīng)常有香客問我乍钻,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任银择,我火速辦了婚禮多糠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘欢摄。我一直安慰自己熬丧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布怀挠。 她就那樣靜靜地躺著析蝴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绿淋。 梳的紋絲不亂的頭發(fā)上闷畸,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音吞滞,去河邊找鬼佑菩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛裁赠,可吹牛的內(nèi)容都是我干的殿漠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼佩捞,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼绞幌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起一忱,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤莲蜘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后帘营,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體票渠,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年芬迄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了问顷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡禀梳,死狀恐怖择诈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情出皇,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布哗戈,位于F島的核電站郊艘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纱注,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一畏浆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狞贱,春花似錦刻获、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氧枣,卻和暖如春沐兵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背便监。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工扎谎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人烧董。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓毁靶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親逊移。 傳聞我的和親對(duì)象是個(gè)殘疾皇子预吆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359