前语

Emmmmm…正式开始文章前先告知个事儿,2019 开年方案那篇文章被我删了。原因没啥特别的,就真的仅仅脸疼…

嘛~ 好久没更 Blog 了,最近笔者正在学习 FlutterK ] + N x u 相关的知识(形似现在学也不{ M C J 5 , y 6 v算特别晚),所以后续或许会有一波接连的 6 P T D * Flu( a H , Etter 相关的更新(关键字已加粗 )。

由于之前我的文章大多是源码剖析相关的,所w : @ s以这次决议换种V @ z x ? Y [办法先从 Flutter 开发的状s j f F :况办理聊起。由于个人才能不足、水平有限,文章中不免有纰漏和错误,加上作业的确比较忙或许没有时刻校验,期望发现的朋友能够在文章下方谈论交流评论(主要是防止误人z m o P C . w子弟,不过谈论应该需求kx上网)。

本文会简略5 # . C E q . s d介绍 Flutter 以及声明式编程思维和代码画风,对比 StatelessWidget & StatefulWidget 这两个重要的 Widget,再聊聊 setState 背面的那些事儿。

索引

  • Flutter 简介
  • 声明式 UI & 呼应式编程
  • Flutter 怎么烘托 WJ B X h a $ , midget
  • StatelessWidget & StatefulWidgeD P c D qt
  • State.setSta* h % L tte 背面的那些事儿

Flutter 简介

Flutter 是 Google 开源的 UI 工具包,帮助开i [ C发者经过一套代码库高效构建多渠道精美运用,支持移动、Web、桌面和嵌入式; Q b * – K Y渠道。

「Write once, ru} i ? + =n anywhe| w z { n y ;re.」始终是大前端开发甚至整个n m Q .程序界的一个永恒的论题。固然 Flutter 的目标也是如此,不过它与之前业界现已存在的 React Native、Xamarin、Hybrid 等框架有何不同?

{% asset_img flutter_system_overview.png %}[ K ; q ~ U = y

从上图能够看到 F; i L G 0 J q $ Hlutter 的设计全体上有三层抽象:

  • 最上层 Framework 封装好了 Material 和 Cupertino 这两种别离对应 Android 和 iOS 官方设计风格的 UI 库,除此之外还包含_ F + ~ | x C #了烘托、动画、制X s !作、手势等等,这一层主要是提供给开发者快捷构建 App 运用的,其在开发时为 JIT、发布时为 AOT 的特性兼顾了开发调试的快捷与线上运转的高# v % b | ;功能;
  • Engine 作为中间层,运用 C/C^ g x h m O 6++ 完成了一套高功能、可移植的 Runtime,屏蔽了渠道间差异的一起支撑了上层的 Flutter 运用。
  • Embedder 由渠道指定言语完成. N } o G y j,提供了 Flutter 所需的事件循环、线程、烘托等根底 API。

Flutter 经过这套机制接管了最底层的系统接口,提供了一整套从底层烘托逻辑到上层开发言语的f # R r Qc 8 f _ g整解决方案,使得它有着逾越 React Natiz & 1 h Fve 的高保真、多端共同{ A z ` M i H * o的体会,和逾越 Web 容器的高功能烘托才能。

声明式 UI & 呼应式编程

声明式 UI

Flutter 状态管理 0x00 - 基础知识及 State.setState 背后逻辑

这张图很好的描绘了声明式 UI 的核心思维,简略来说便是经过 state 作为入参依据现已写好的构建 func 就能得到咱们想要的$ 8 I n A D # S 4 UI 作用。举个 更直观:

默许的 Flutter Demo 界面:

{% asset_img flutter_default_demo.png %}

对应的代码:

return Scaffold(
appBar: AppBar(
title: TextU ^ %(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignmenw ` 2t.cenP % . s % 4 ~ter,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(x / v E e E
'$_counter',
style: Theme.of(context).tD a ` | 3 M V pextThemeg Q . g.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
// _i+ , L : &ncrementCounter 内部逻辑可疏忽
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);

In ff K d L 0lutter, everything is a widget.

Emm^ C b Pmmm…能够留意到上面的代码中 children: <Widget>[...] 一行,Flutter 开发中便是经过{ p [ u 5 h . q E这种办法嵌套组合 Widget 来描绘用户界面的。从这一角度讲,Widget 是对一部分用户界面的不可变描绘。

默许的 Flutter Demo 跑起来很简略,引荐咱们亲自体会。简略说一^ n d `下我的感受,声明式 UI 的优势很大,具体表现为只需求在写 UI 时将不同 stas C K ; / U ,te 对应的 UI 展示考虑并描绘清楚就能够省去后续 sth a R ! 3 Y [ B 2ate 改变时K H N i R # H ^命令式 UI 需求手动办理视图层级(新增或删去视图元素)和更新特点(颜色、字号等)等费事。

值得一提的是 SwiQ n r IftUI 也运用了声明式 UI 的思维(声明式 UI 才是未来)。

呼应式编程

呼应式编程 ,在计算机范畴,呼应式编程是一个专注于数据流和变化传递的异步编程范式。
这意味着能够运用编程言语很容易地表明静态(例如数组)或动态(例如事件发射器)数据流,
并且在相关的履行模型中,存在着可推断的依赖联系,这个联系的存在有利于主动传播与数据流有关的更改。

呼应式编程关于咱们来说应该早0 J M D k已不是什么新鲜事$ = 6 ) { s物了,单在移动客户端范畴就有诸如 ReactiveCocoa、RxSwift、RxJava、RxXxx…简略来说这种编程思维或许说范式下开发者只需重视或许存在的数据状况以及与之对应的逻辑从而大大减轻了维护这些对应联系与状况细节的作业负担。f f P n Z由于用过的人都说好,所以现在逐& h S 5 8 y ! u渐被推为移动客户端甚至整个6 4 大前端的干流编程思维。

不难看出其实声明式 UI 和呼应式编程从思维上是彻底符合的,咱们只需求将数据流对应到 UI=f(state) 公式中的 state 就能够了。

FlutterA U Q . = + l [ 怎么烘托 Widgc A q x J d l ]et

Flutter 状态管理 0x00 - 基础知识及 State.setState 背后逻辑

在介绍 StatelessWidgetStatefulWj u A ~ ; N 2 5idgJ b } ? H B s P +et 之前先要了解一下 Flutter 的烘托模型,简略来讲 Flutter 在烘托 Widget 时用到三棵树:

  • Wide ] l T eget,担任描绘 Elemen* x D I / ^ wt 的配置。
  • ElemeJ t k V d p W v )nt,担任办理 Widget 和 RenderObject 的生命周期。
  • RenderObject,担任控制尺寸,布局和制作作业。

一言以蔽之,Element 会依据咱们书写的 Widget 对其的配置描绘来生成并办理对B 3 a R X应的 Element 与 RenderObjek 5 C s q k ,ctp S ;,由 RenderObject 担任终究的制作作业。

NOTE: Element 会依b b o v P据 Widget 的描绘最大程度复用现有 Element 与 RenderObject 以提高功能。

StU a t 4 ? k h 9 zateleT / ?ssWi+ = B _ &dget & StatefulWidget

StatelessWidget

StatelessWid- k ] = (get, A widget that does nh V , + _ 9 ,ot require mutable state.

StatelessWidget 如其名 Stateless,它不需求? K #追寻 StW ^ R = 2ate 并依据 StaF ` R /te 更新 UI,所以一般 SW T x etl w + x N , : ateC ? h h - ( X E FlessWidget 内部的w t l X 1 [特点用 final 润饰声明且构造办法一般以 const 润饰。

NOTE: const 润饰的 Widget 构造办法运用时能够提效。

StatefulWidget

StatefulWidget,v S R A D [ k A widget that has mutable state.

Statefulw / w kWidgetState` f 3 v X l O - 1lessWidgetI [ i 0 ] m R p { 相同,也能够} 3 k Y e = – 5经过一系列(组合嵌套)其他更具体描绘 UI 的 Widget(比如 Text)来构建部分用户界面。差异在于 Stateful,即它需求追寻 State 并依据 State 更新 UI。

差异

StatelessWidgetStatY o 7 n L V S A !efulWidget 大概是咱们用V r , m ; y Flutter 技能栈开发 App 时最常打交道的两个 Widget 了。

共同点:

  • 这两个 Widget 都能够用来经过对其他一系列 Widget 构建完成一部分用户界面的封装。

不同点:

  • S~ ? C wtatelessW+ . G v H _idget 更适用于一些$ | 9 i 6 D不需经过用户交互或其他原因经过 State 控制更新的o Q % a o . * 8 UI 封装。
  • StatefulWidget 更适用于追寻某个会依据用户交互或其他要素影响的 State,并依据最新的 State 实时更新的 UI 封装。

此外,Flutter 关于 StaI 0 * d l 6 $ { 6teLessWidgetStatefulWidget 的制作也有差异:

  • StatelessWidget 经过 StatelessElement.build 触发 build
  • Stat@ ( n ~ F t ]efulWidget 经过 StatefulElement.bui+ 9 ~ld 触发 State.build

State.setState 背面的那些事儿

NOTE: 下面剖析的 State.setState 源码版本为 Flutter SDK v1.9.1+hotfix.6。

咱们还以 Flutter 默许 Demo 来剖析,在 Demo 中咱们点击界面右下角的 Floa# K G & N 4 %tingActionButton (便是蓝色带有 +2 1 8 E ` } 号的圆形按钮)之后会改写界面,屏幕中间的 Text Widget 会显现按钮被按下的次数。

Flutter 状态管理 0x00 - 基础知识及 State.setState 背后逻辑

Floay ! ~ b 7 } ; =tingActionButton 点击之后调用了 _incrementCounter 办法:

class _MyHomePageState extends State<MyHo^ 2 K 8 UmePage>, M u % & & {
int _cou+ W e $ Tnter = 0;
void _incrementCounter() {
setSt 6 { # Ktate(() {
_counter++;
});
}
...
}

_MyHomePageState._incrementCounter 内部逻辑也仅仅是调用了 State.setState 罢了。正如 Flutter 所宣扬的公式 UI = f(state) 相同,开发者只需求调用 State.se8 ` = ~ `tState 传入 VoidCallback 内部写好相关数据更新逻辑即可更新 UI,那么 Flutter 是怎么做到这一点的呢?

S T q 5 & j q ( Dtate.setState 背面逻辑

Flutter 状态管理 0x00 - 基础知识及 State.setState 背后逻辑

State.setState 背面调用嵌套1 } ? 3较多,实际_ H y 3 $ N所做的工作理解起来却很简略。如不喜在文内阅览源码可直接跳转至「总结 State.set9 D q %State 背面逻辑」末节看相关逻辑总结。

State.setState

@optionalTypeArgs
abstract class State<T extends StatefulWidget>q % W extends Diagd y T ) k 6 ) dnosticable {
...
@protected2 E u M . l
void setState(VoidCallbackr r m % N 0 ) fn) {
/X + H ~ D d d/ assert ...
final dynamic result = fn() as dynamic;
// asse,   z 3 Q O W -rt ...
_element.markNeedsBuild();
}
...
}

可见– z Y F s x State.setState 内除掉断言的逻辑只有两行代码:

  • 履行入参 VoidCallback 逻辑,Demo 中对应 _counter++;
  • 履行 _element.markNeedsBuild();

Element} R d ( D M h.markNeedsBuild

abstract class Element extends Diagno0 # m h XsticableTree implements BuildContext {
...
void markNe| H  8 m k : hedsBuild() {
// assert ...
if (!_active)
return;
//, R ^ | m assert ...
if (dirty)
return;
_dirty = true;
owner.scheduleBuildFor(this);
}
... b N
}

Element.markNeedsBuild 内部的逻辑也很简略明了:

  • 如此 Element 不再活泼则z o D c直接 return 无需做任何事
  • 如此 ElX 5 P I [ w Hement 现已被标记为 dirty 也无需重复标记直接 return
  • 以上P g U X K y r V条件不满足时说明此 En 4 E s , 5lement 是活泼且需求从头构建的,所以标记为 dirtyj Y [ ) P 后调用K E – V ) owner.scheduleBuildFor(this);

BuildOwner.scheduleBuildFor

class BuildOwner {
...
void scheduleBuildFor(Element ey ! F Plement) {
// assert ...
if (!_1 Z FscheduledFlushDirtyEc f T 8lements && onBuildScheduled != nullP o f c A F D |) {
_scheduledFlushDirtyElements = true;
onBuildScheduled();
}
_dirtyElemenN ) k m * U zts.add(element);
element._inDirty& | . u U 7 nList = true;
// assert ...
}
...
}

BuildOwner.scheduleBuildF= J T / 3 ] :or(this); 内部逻辑也不杂乱:

  • !_scheduledFlushDirtyElements && onBuildScheduled != n] _ = Y b 1ull 能够理解为假如还没有组织过改写此 Bue t r 5 M F 6i+ q yldOwner 下被标记为 Dirty 的 Elements 且组织构建的逻辑不为空,满足以上条件则将此 BuildOwner 组织改写 Dirty Elements 的标记置为 true
  • 将传入的 Element 加入此 Build7 7 y + 5 6 TOwner 办理的 _dir[ 7 y y y I / :tyElements 并将此 Element _inDirtyList 标记为 true

BuildOwner.onBuildScheduled

其实 onBuildScheduled(); 跟下去是 BuildOwner 内部特点 onBuildScheduled,类型为6 B u # VoidCallback,在 WidgetsBinding.in, , } W P F { # titInstanh ( _ces 时被赋值为 Widgei % T atsBinding._handleBuildScheduled:

mixin WidgetsBinding on BindingBase, SchedulerBinding,X % _ 2 b GestureBin5 ? / 6ding, RendererBinding, SemanticsBinding {
...
@override
void initInstances() {
...
buildOwner.onBuildScheduled = _handleBuildScheduled;
...
}
...
}

WidgetsBinding 能够理解为 Widget 层与 Flutter engine 之T W D ^ Z , V间的胶水层,篇幅原因不打开讲,咱们直接看 Widgc K + m C F X #etsBinding._handleBuildSchedH C Euled

mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
...
void _haO | d a n d :ndleBuildScheduled() {
//K Q X L F | k k assert ...
ensure/ j W @ XVisualUpdate();
}
...
}

SchedulerBinding.ensa U ? B X Q 5 3 QureVisualUpdate

mixin SchedulerBinding oL u n BindingB+ E u j u g 8 !ase, ServicesBinding {
...
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
ret: [ 0 b H 3urn;
}
}
...
}

这儿经过其时 SchedulerPhase 的状况进行取舍,仅在 SchedulF _ M ; y I U berPhase.idle & Scho - E V J c h k bedulerPhase.postFrameCallbacks 时调用 scheduleFrame();

SchedulerPhase.schl f X # s 8eduleFrame

mixin SchedulerBinding on BindingBase, ServicesBinding {
...
void scheduleFrame()F 5 | q G {
if (_hasScheduledFrame || !_framesEnabled)
return;
// a} z ! $ @ssert ...
window.scheduleFraP { tme();
_hasScheduledFrame = true;
}
...
}

这儿的逻辑更简略:

  • _hasScheduledFrame || !_framesEnabled 可理解为假如现已组织过 Frame 或其时 Frame 不可用,这种情况直接 return
  • 不满足上述情况则调用 window.scheduleFrame(); 然后标记 _hasScheduledFrametrue

Window.scheduleFrame

void sched~ 0 ^uleFrame() native 'Window_scheduleFrame';

这儿的 Window.scheduleFrame 是 Native+ & X 4 W } S r 办法,能够理解为m # x =经过 Window 与 Flutter engine 打交道,注册了 VSync 回调。

总结 State.setState 背面逻辑

Flutter 状态管理 0x00 - 基础知识及 State.setState 背后逻辑

简略来说 State.W { 0 v /setState 就干了这么几件事:

  • 履行入I $ B , v U Y XVoidCallback 逻辑,即履行开发者写好的信息改变逻辑h W C % { 9 ] E
  • 尝试将其时 StatefulWidget 对应的 StatefulElement 标记为 dirty
  • 经过 BuildOwner.onBuildI $ ] ;ScheduledSchedulerPhase.scheduleFraV J R d = Bme 再到 Window.scheduleFrame 一步步完成了 VSync! r N Y e k # 的回调注册

留意上面 State.setState 第二条主要逻辑便是把 State 相关的 Element 标记_ ~ Y 8 ~ E a G KdM 7 _ G yirty。在 VG K $ XSync 回调后会经过 Native 到 Flutter engine 调用 Flutter _dr; _ - ( fawFrame 办法,将之前标记为 d[ 0 R n J _ ) P Tirty 的 Element 从头构建,终究会履行到开发者熟悉的 State.build 办法。

State.build 是怎么被履行的

这儿由于篇幅原因W , x简略描绘下后续逻辑,究竟本文重点不在于 Flutp / } h x n K 2ter 怎么烘托 Widget:

  • State 是开发者重写 StH _ l |atefulWidget.createState 回来的
  • State 对应的 Elemed 3 6 A G g ;nt 是 StatefulWidget.createEM u - | 5 & S Flement 回来的,类型为 St+ 2 + M S 2ateful5 8 d b yElement
  • Stat^ 7 p z V ] ^efulElement 被标记为 dirty 后在 FluttM d p 4 1 zer _drawFrame 时从头构建会调用 StateX 6 E w E |fulElementz : b c J 2.build,其源s T O z码为 Widget build() => state.builE g s S y Fd(this);
  • statK V F O % je.build(this)Z S * - { # 中的 state 便是咱们的 State,开发者写的 State.build 办法就这样被履行了

总结

本文是 Flutter 状况办理的开篇,为了照料一些还没来得及学习 Flutter 或许Y ; r , :刚入门 Flutter 的初心者所以文章从前到后做了一些衬托介绍过渡。文章内容也比较浅显易懂l ! ] = x M,根本上是围绕 Flutter 创建 App 默许的 Demo 来r a 2 : F z S 6打开讲解一些根本的 Flutter 知识点:

  • Flutter 烘托 Widget 的三颗树概念
  • StatelessWidget & StatefulWidget
  • State.W y u R [ 6 esetState 背面逻辑

由于状况办理这个论s G O n V | e s .题非常大且杂乱,文章由于篇幅原因就到这儿,后续的文章(假如有的话)应该不会再花大篇幅做 Flut` n & n J P g A %ter 根底知识的衬托和过渡了(可是该有的前置知识点必定还会有)。时刻紧张,文章不免呈现错误,估量自己也没有时刻做校对了,有t o 0 . O V @问题还望在谈论区提醒。

扩展补充

写到这儿我意识到 Flutter 状况办理这个论G n [题下能够引出很多值得发掘的内[ e , 8 s 4 w 5 rd Z y 1{ & M = 4并且笔者坚信声明式 UI + 呼应式编程会是未来移动客户端甚至整个大前端的干流编程范式。鉴于3 n I P |此,后续的文章方案围绕 Flutter 状况办理逐步深挖,内容也将在] * + H I q z Flutter 技能栈k 6 x的根底上做适度横向对比。

Emmmmm…后续# N 6的文章将会发布在我的《FluO ^ Zttei f } 8 @ A ;r 状况办理》小专栏。至于专栏定价嘛,现在随意定了c I 3 & ) 69(作为小通明的我瑟瑟发G x N F s + =抖 ing~ y a c @ ! f)。其实倒也不是很在意能卖出多少份,或许从中收到多少钱,究竟自己写文章的精力O O J r U W 7 t拿去随意接个朋友的私活都应该比这次经过小专栏赚到的多。比起收入更多的是对自己的鞭笞吧,究竟是收费的– Y # k ( v ~东西,只要有一个人订阅了这个专栏就意味着对我的必定,我就有责任把这个方向发掘的更深化,一起文章质量也应该比博客内容更高。

其实早在上一年就有幸被小专栏的开发者@安卓大王子邀请去小专栏写文章,不过其时自己感觉没什么好的内容方向所以仅仅开通了专栏但没有任何输出。时至今日总算找到了一个值得深挖的方向,自然而然想到了这个渠道。个人感觉小专栏这个渠道没有太重的商业化滋味,这点很讨喜,在微信上面的阅览体会也比自己发大众号要好一些,更难能可贵的是省去了发大众号的繁琐操作流程。或; g ` ` ! G O : D许正是商业化氛围弱的原因,上面的文章质量还都挺不错的,引荐各位读者朋{ k w –友能够上去试读或许写作。