Flutter UI 绘制与InheritedWidget解析
Flutter UI 绘制与InheritedWidget解析
Flutter的Widget
分为StatefulWidget
和StatelessWidget
,二者都继承自Widget
。
此外还有一种用来传输数据的Widget
——InheritedWidget
,与上述两者不太一样的是,他的继承关系是:InheritedWidget
→ProxyWidget
→Widget
。
Flutter的渲染流程如图:
可以简单理解为, Widget
是配置信息,Element
代表在树中详细的位置,而RenderObject
则是实际渲染的对象。
StatelessWidget
和StatefulWidget
在创建之后就不会再变化,而StatefulWidget
因为有State
,所以可以在State
调用setState()
方法之后,重新执行State
的build()
方法,从而更新界面。
如果Widget
是const
的,那么他就不会被rebuild
。
Widget Rebuild的过程
以StatefulWidget为例:
调用
setState()
方法,会调用对应的Element
的markNeedsBuild()
方法,通过BuildOwner
的scheduleBuildFor(Element element)
方法将当前Element
标记为dirty
,以便在下次屏幕刷新时安排rebuilt
。下一帧屏幕刷新,调用
BuildOwner
的buildScope(Element context, [ VoidCallback? callback ])
方法。这个方法会遍历_dirtyElements
中所有dirty
的element
执行element.rebuild();
方法,在其内部调用了Element
的performRebuild()
方法。Element
的performRebuild()
方法因各个Element
的实现而异:StatelessElement
、InheritedElement
:与父类ComponentElement
保持一致StatefulElement
:判断有需要时调用state.didChangeDependencies();
,其余与父类ComponentElement
保持一致
而ComponentElement
的performRebuild()
主要做了2件事:
(1)built = build();
;(2)_child = updateChild(_child, built, slot);
在这其中build()
:
StatelessElement
:build() => widget.build(this);
StatefulElement
:build() => state.build(this);
InheritedElement
:build() => widget.child;
updateChild
会判断以下几种情况:
newWidget == null | newWidget != null | |
---|---|---|
child == null | return null | return new Element |
child != null | remove old child, return null | Old child updated if possible, returns child or new Element |
其中,old child updated
的时候调用的是child.update(newWidget);
方法会触发Widget
的rebuild()
。
这样就完成了一次Rebuild。
InheritedWidget的Rebuild过程
InheritedWidget
是持有状态的Widget
,他的子Widget
可以通过他来获取这些状态。
一般来说,InheritedWidget
持有的状态是final
的,如果要更新状态,就需要在其外部包裹一个StatefulWidget
,通过StatefulWidget
的State.setState()
来触发InheritedWidget
重建(实际上InheritedElement
没有重新创建),从而更新那些依赖了InheritedWidget
的子Widget
。
下图是一个被StatefulWidget
包裹的InheritedWidget
在setSate(){}
方法执行后的流程图:
当外层StatefulWidget
的Element
执行到updateChild(child,build,solt);
会调用InheritedElement
的update()
方法。
这个方法内部会调用updated(oldWidget)
方法,在内部通过notifyClients(oldWidget);
方法,通知原先的InheritedElement
的_dependents
,将其标记为dirty
,准备rebuild
。
在此之后,update()
方法还会将当前Element
标记为dirty
,通过调用rebuild();
执行performRebuild();
在performRebuild()
方法中:
built = build();
中的build
方法:build() => widget.child;
实际上取了widget
的child
。- 然后执行
_child = updateChild(_child, built, slot);
这个过程与普通Widget
一致。
需要注意的是,updateChild
中,如果子Widget
不是const
(或者被InheritedWidget
外层的widget
/state
之类的持有)就会被认为built!=_child
从而导致InheritedWidget
的子Widget
重建。导致的结果就是:虽然InheritedWidget
的确只标记了那些依赖了他的Widget
,但是由于直接子Widget
要重建,所以还是所有的非const Widget
都重建了。
InheritedWidget的获取方式
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
获取指定类型的InheritedWidget
,并且将自己注册到此Widget
,以便当该Widget
变化的时候,自己也能rebuilt
。复杂度O(1)
。T? findAncestorWidgetOfExactType<T extends Widget>();
只获取指定类型的Widget
,包括InheritedWidget
,仅获取该Widget
执行一些操作,通常用在interaction event handlers
之类中。复杂度(O(N)
。
代码示例
根据上述理论,创建一个InheritedWidget
来传递数据:
1、AppColor.dart
一个持有color
的InheritedWidget
。
1 | class AppColor extends InheritedWidget { |
2、定义一些类,使用或未使用到InheritedWidget
:
1 | class NoName extends StatelessWidget { |
3、接下来实现一种基础的使用InheritedWidget
的方法,这种方法会在InheritedWidget
更新的时候,rebuilt InheritedWidget
下面的所有子类,无论他们是否使用到了InheritedWidget
(原因是上面说到的Flutter rebuild的机制导致的,实际上InheritedWidget
本身只标记了ColorfulContainer
为dirty
)。
1 | class AlwaysRebuildWidget extends StatefulWidget { |
4、接下来实现一种使用InheritedWidget
的方法,当InheritedWidget
更新的时候,只会更新那些在InheritedWidget
这里注册依赖了的Widget
。
1 | class SelectiveRebuildWidget extends StatefulWidget { |
参考资料
Using Inherited Widget In Flutter
【Flutter学习】之Widget数据共享之InheritedWidget 梁飞宇
Managing Flutter Application State With InheritedWidgets