ps: 文中flutter源码版本 1.0.0
ProxyWidget
代理控件,内部对原本的控件进行了包装处理
abstract class ProxyWidget extends Widget {
const ProxyWidget({ Key key, @required this.child }) : super(key: key);
final Widget child;
}
这类控件的ProxyElement在build的时候直接将child返回了
其有2个比较重要的子类, ParentDataWidget和InheritedWidget
ParentDataWidget
用在多孩子控件中,主要是获取父控件的ParentData
这个控件用途比较广,如Positioned,可能你没用过,但布局控件Stack总有用过,他们是配套使用的
Positioned之于Stack,就好比Colum/Row之于Expanded(Flexible),就好比Table之于TableCell,就好比CustomMultiChildLayout之于LayoutId
我们就拿中文网上那个例子来分析:
Stack(
alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式
children: <Widget>[
Container(child: Text("Hello world",style: TextStyle(color: Colors.white)),
color: Colors.red,
),
Positioned(
left: 18.0,
child: Text("I am Jack"),
),
Positioned(
top: 18.0,
child: Text("Your friend"),
)
])
为什么Positioned的位置不受Alignment.center的影响?
我们先看Positioned:
//光看泛型就知道与Stack渊源不浅
class Positioned extends ParentDataWidget<Stack> {
const Positioned({
Key key,
this.left,
this.top,
this.right,
this.bottom,
this.width,
this.height,
@required Widget child,
//断言,意思是6个属性必须传至少一个
}) : assert(left == null || right == null || width == null),
assert(top == null || bottom == null || height == null),
super(key: key, child: child);
//省略了多个命名构造函数与工厂构造函数
...
final double left;
final double top;
final double right;
final double bottom;
final double width;
final double height;
//继承方法,这个肯定就是影响的关键所在了
@override
void applyParentData(RenderObject renderObject) {
assert(renderObject.parentData is StackParentData);
final StackParentData parentData = renderObject.parentData;
bool needsLayout = false;
//判断父类传递的与自身属性的值是否一样,不一样则给父类的赋值,并且父控件重新摆放
//注:默认值是为null的
if (parentData.left != left) {
parentData.left = left;
needsLayout = true;
}
if (parentData.top != top) {
parentData.top = top;
needsLayout = true;
}
if (parentData.right != right) {
parentData.right = right;
needsLayout = true;
}
if (parentData.bottom != bottom) {
parentData.bottom = bottom;
needsLayout = true;
}
if (parentData.width != width) {
parentData.width = width;
needsLayout = true;
}
if (parentData.height != height) {
parentData.height = height;
needsLayout = true;
}
if (needsLayout) {
final AbstractNode targetParent = renderObject.parent;
if (targetParent is RenderObject)
targetParent.markNeedsLayout();
}
}
//我们只需弄懂原理,这个肯定与debug有关,不考虑的
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
...
}
}
StackParentData是什么?这里只是存储值,与Positioned的属性对应
class StackParentData extends ContainerBoxParentData<RenderBox> {
double top;
double right;
double bottom;
double left;
double width;
RelativeRect get rect => RelativeRect.fromLTRB(left, top, right, bottom);
set rect(RelativeRect value) {
top = value.top;
right = value.right;
bottom = value.bottom;
left = value.left;
}
//使用Positioned的话,因为断言,6个属性必须传至少一个
bool get isPositioned => top != null || right != null || bottom != null || left != null || width != null || height != null;
@override
String toString() {
...
}
}
很好,大致思路已经清楚了,那么applyParentData(RenderObject renderObject)方法中,这个renderObject是谁?什么时候传递的?renderObject.parentData又是什么时候赋值的?
我们现在从头开始分析:
//Stack是一个MultiChildRenderObjectWidget
class Stack extends MultiChildRenderObjectWidget {
...
@override
RenderStack createRenderObject(BuildContext context) {
return RenderStack(
alignment: alignment,
textDirection: textDirection ?? Directionality.of(context),
fit: fit,
overflow: overflow,
);
}
...
}
既然是MultiChildRenderObjectWidget,根据之前的分析,我们从其的mount方法开始:
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
//length个数是3,建立空列表
_children = List<Element>(widget.children.length);
Element previousChild;
//循环开始查找
for (int i = 0; i < _children.length; i += 1) {
//widget.children[i],这里我们只找Positioned
//这里传递前一个为了建立单链表
final Element newChild = inflateWidget(widget.children[i], previousChild);
//列表保存
_children[i] = newChild;
previousChild = newChild;
}
}
进入inflateWidget方法
@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
...
final Element newChild = newWidget.createElement();
assert(() { _debugCheckForCycles(newChild); return true; }());
newChild.mount(this, newSlot);
assert(newChild._debugLifecycleState == _ElementLifecycle.active);
return newChild;
}
Positioned的createElement()是ParentDataElement<Stack>(this),因此找其的mount方法
@override
void mount(Element parent, dynamic newSlot) {
//都是断言,省略
...
super.mount(parent, newSlot);
}
找到ParentDataElement的父类的父类ComponentElement(抽象类ProxyElement未实现此方法)
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_active);
_firstBuild();
assert(_child != null);
}
void _firstBuild() {
rebuild();
}
这一系列和前面分析RenderObject(事件分发)是一样的,最后会传递到Text里RichText的LeafRenderObjectElement中,所以从它的mount方法
//父类RenderObjectElement中的方法
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
assert(() { _debugUpdateRenderObjectOwner(); return true; }());
assert(_slot == newSlot);
//绑定RenderObject
attachRenderObject(newSlot);
_dirty = false;
}
跟进attachRenderObject(newSlot)方法
@override
void attachRenderObject(dynamic newSlot) {
assert(_ancestorRenderObjectElement == null);
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
final ParentDataElement<RenderObjectWidget> parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
_findAncestorRenderObjectElement() 是为了找到前一个RenderObjectElement,即Stack中的MultiChildRenderObjectElement
RenderObjectElement _findAncestorRenderObjectElement() {
Element ancestor = _parent;
//循环找到一个为RenderObjectElement的父Element
while (ancestor != null && ancestor is! RenderObjectElement)
ancestor = ancestor._parent;
return ancestor;
}
至于_parent是哪赋值的呢,之前的inflateWidget方法里有一个newChild._activateWithParent(this, newSlot);
void _activateWithParent(Element parent, dynamic newSlot) {
assert(_debugLifecycleState == _ElementLifecycle.inactive);
_parent = parent;
...
}
a. parentData赋值
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot)即调用MultiChildRenderObjectElement中的方法:
@override
void insertChildRenderObject(RenderObject child, Element slot) {
final ContainerRenderObjectMixin<RenderObject, ContainerParentDataMixin<RenderObject>> renderObject = this.renderObject;
assert(renderObject.debugValidateChild(child));
//这个renderObject是Stack的RenderStack
renderObject.insert(child, after: slot?.renderObject);
assert(renderObject == this.renderObject);
}
insert不仅加入了列表,还执行了重新摆放请求
void insert(ChildType child, { ChildType after }) {
...
adoptChild(child);
_insertIntoChildList(child, after: after);
}
跟进adoptChild方法:
@override
void adoptChild(RenderObject child) {
assert(_debugCanPerformMutations);
assert(child != null);
//设置ParentData
setupParentData(child);
super.adoptChild(child);
markNeedsLayout();
markNeedsCompositingBitsUpdate();
markNeedsSemanticsUpdate();
}
再跟进setupParentData方法
void setupParentData(covariant RenderObject child) {
assert(_debugCanPerformMutations);
if (child.parentData is! ParentData)
child.parentData = ParentData();
}
很明显不对,我们要的不是ParentData()对象,而是StackParentData(),会不会是RenderStack重写了该方法
@override
void setupParentData(RenderBox child) {
if (child.parentData is! StackParentData)
child.parentData = StackParentData();
}
NICE,已经解决一个问题了!
b. applyParentData调用流程
ParentDataElement<RenderObjectWidget> _findAncestorParentDataElement() {
Element ancestor = _parent;
while (ancestor != null && ancestor is! RenderObjectElement) {
//寻找属于ParentDataElement的父Element
if (ancestor is ParentDataElement<RenderObjectWidget>)
return ancestor;
ancestor = ancestor._parent;
}
return null;
}
基本和前面那个一样,就是找到对象不一样,找到了Positioned的ParentDataElement<Stack>,然后调用_updateParentData(parentDataElement.widget)方法
void _updateParentData(ParentDataWidget<RenderObjectWidget> parentData) {
//parentData就是Positioned,renderObject则是Text的RenderParagraph
parentData.applyParentData(renderObject);
}
OK,流程就走完了
InheritedWidget(填坑)
数据共享的控件
一直不明白这控件有什么优势,要实现其类似的效果,方式其实有很多
可能最大的优点就是使用起来比较简单吧,而且源码结构上都用上了它,既然官方都提供了,那还不乖乖的使用
还是以中文网上的简单计数器例子为例,进行源码分析:
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Widget child
}) :super(child: child);
int data; //需要在子树中共享的数据,保存点击次数
//定义一个便捷方法,方便子树中的widget获取共享数据
static ShareDataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(ShareDataWidget);
}
//该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(ShareDataWidget old) {
//如果返回false,则子树中依赖(build函数中有调用)本widget
//的子widget的`state.didChangeDependencies`会被调用
return old.data != data;
}
}
可以注意到有2个重要的方法:inheritFromWidgetOfExactType和updateShouldNotify
class InheritedWidgetTestRoute extends StatefulWidget {
@override
_InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
}
class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: ShareDataWidget( //使用ShareDataWidget
data: count,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: _TestWidget(),//子widget中依赖ShareDataWidget
),
RaisedButton(
child: Text("Increment"),
//每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
onPressed: () => setState(() => ++count),
)
],
),
),
);
}
}
每点击一次按钮,就会调用一次setState,InheritedWidgetTestRoute就会rebuild一次,再次调用build方法,此时ShareDataWidget的data将赋予新的count值,根据前面的分析,可以知道会调用update方法
abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({ Key key, Widget child })
: super(key: key, child: child);
@override
InheritedElement createElement() => InheritedElement(this);
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
寻找InheritedElement中的update方法,在其父类ProxyElement中找到
@override
void update(ProxyWidget newWidget) {
final ProxyWidget oldWidget = widget;
assert(widget != null);
assert(widget != newWidget);
super.update(newWidget);
assert(widget == newWidget);
updated(oldWidget);
_dirty = true;
rebuild();
}
调用InheritedElement的updated方法
@override
void updated(InheritedWidget oldWidget) {
//这里调用updateShouldNotify,如果数据不同,则进行更新
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
继续根据super.updated(oldWidget)
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
继续调用InheritedElement的notifyClients方法
@override
void notifyClients(InheritedWidget oldWidget) {
//省去断言
...
//执行列表遍历
for (Element dependent in _dependents.keys) {
//省去断言
...
notifyDependent(oldWidget, dependent);
}
//调用元素的didChangeDependencies方法
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
这里会有疑问:_dependents是什么?数据是什么时候添加的?
final Map<Element, Object> _dependents = HashMap<Element, Object>();
_dependents是一个HashMap键值表,至于怎么赋值的我们要看另一个方法,BuildContext是一个接口,它的子类只有Element,所以查找其的方法:
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
assert(_debugCheckStateIsActiveForAncestorLookup());
//查找表中targetType类型对应的InheritedElement
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
_inheritedWidgets的值是什么时候赋值的稍后分析,先分析完当前这个:
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
//更新数据
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
跟进updateDependencies方法:
@protected
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
已经找到了_dependents的赋值,只有key,value都是null。再回到前面,ancestor值找的是谁?_inheritedWidgets值是什么时候赋的?
InheritedElement创建时会调用mount方法:
//Element中方法
void mount(Element parent, dynamic newSlot) {
...
_updateInheritance();
assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
}
InheritedElement的_updateInheritance()中进行赋值
@override
void _updateInheritance() {
assert(_active);
//父类向下传递map
final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
//有值则拷贝,无值则初始化
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
//将当前InheritedElement加入到map中
_inheritedWidgets[widget.runtimeType] = this;
}
HashMap的from拷贝方法:
factory HashMap.from(Map other) {
Map<K, V> result = new HashMap<K, V>();
//循环逐值拷贝
other.forEach((k, v) {
result[k] = v;
});
return result;
}
至此就分析完了,其实也就是内部有一个HashMap保存着当前的对象,然后通过该对象得到它要保存的数据
因为key是不能重复的,所以只能保存一个值,也就是说当有多个相同的InheritedWidget,map中的value进行覆盖,只保留最后一个。对于实际的InheritedWidget来说只取最近的
InheritedWidget的作用域只能包括自己及自己的子节点,所以InheritedWidget在树中只能向下传递
上一篇:深入分析Widget 1
这里有个使用的实例:天气查询APP
网友评论