Flutter笔记-深入分析Widget 1

ps: 文中flutter源码版本 1.0.0


widget关系树

widget基本分为StatelessWidget、StatefulWidget、RenderObjectWidget和ProxyWidget 4类型(子类PreferredSizeWidget是一个接口)
先从最简单的开始分析:

StatelessWidget

1. 这是什么?

StatelessWidget是一个无状态的控件,之前我们只关心其的build方法,那么其的build是什么时候调用的呢?

abstract class StatelessWidget extends Widget {

  const StatelessWidget({ Key key }) : super(key: key);

  @override
  StatelessElement createElement() => StatelessElement(this);

  @protected
  Widget build(BuildContext context);
}

class StatelessElement extends ComponentElement {
  StatelessElement(StatelessWidget widget) : super(widget);
  //这种写法在flutter中比较常见
  //widget就是StatelessWidget的createElement()方法中传递的this
  @override
  StatelessWidget get widget => super.widget;

  //调用StatelessWidget中的build
  @override
  Widget build() => widget.build(this);

  @override
  void update(StatelessWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    _dirty = true;
    rebuild();
  }
}

这个问题就转换为什么时候调用StatelessElement中的build方法
带着这个问题,我们从头开始捋

2. 定义一个起点

flutter的main函数初始都是这么写的,ok,我们就按这个顺序分析下来

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp();
  }
}

后续过程中很多源码其实之前已经分析过,这里还是过一下。先看runApp方法:

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..scheduleWarmUpFrame();
}

我们只关心直接的MyAPP控件是什么时候build的,所以直接看attachRootWidget(app)

void attachRootWidget(Widget rootWidget) {
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget
  ).attachToRenderTree(buildOwner, renderViewElement);
}

创建了一个RenderObjectToWidgetAdapter控件,并调用了attachToRenderTree方法

class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
  RenderObjectToWidgetAdapter({
    this.child,
    this.container,
    this.debugShortDescription
  }) : super(key: GlobalObjectKey(container));

  final String debugShortDescription;

  @override
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

  @override
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;

  @override
  void updateRenderObject(BuildContext context, RenderObject renderObject) { }

  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement<T> element]) {
    //不存在则创建一个
    if (element == null) {
      owner.lockState(() {
        //创建了一个RenderObjectToWidgetElement
        element = createElement();
        assert(element != null);
        element.assignOwner(owner);
      });
      owner.buildScope(element, () {
        //进行登记
        element.mount(null, null);
      });
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element;
  }

来到RenderObjectToWidgetElement中的mount方法

@override
void mount(Element parent, dynamic newSlot) {
  assert(parent == null);
  super.mount(parent, newSlot);
  _rebuild();
}

void _rebuild() {
  try {
    //这里传递的是widget.child,widget就是RenderObjectToWidgetAdapter
    //child即是MyApp控件
    //参数_child为未更新前的child,初始为null
    _child = updateChild(_child, widget.child, _rootChildSlot);
    assert(_child != null);
  } catch (exception, stack) {
    ...
    );
    ...
  }
}

updateChild在基础父类Element中

@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  ...
  //本次的child是未null的,只要再次调用rebuild才有值
  if (newWidget == null) {
    if (child != null)
      deactivateChild(child);
    return null;
  }
  if (child != null) {
    if (child.widget == newWidget) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      return child;
    }
    //条件,必须新旧控件相同
    if (Widget.canUpdate(child.widget, newWidget)) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      //执行这一步的条件必须是child有值,但第一次调用build方法时,child为null,只有第二次后才有值
      //这里会调用Element中的update方法
      child.update(newWidget);
      ...
      return child;
    }
    deactivateChild(child);
    assert(child._parent == null);
  }
  return inflateWidget(newWidget, newSlot);
}
//类型和key相同则表示同一个控件
static bool canUpdate(Widget oldWidget, Widget newWidget) {
  return oldWidget.runtimeType == newWidget.runtimeType
      && oldWidget.key == newWidget.key; 
}

渲染控件,并且向下遍历

@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
  //省略断言判断  
  ...
  //newWidget即是MyApp,是一个StatelessWidget,所以创建了一个StatelessElement
  final Element newChild = newWidget.createElement();
  assert(() { _debugCheckForCycles(newChild); return true; }());
  //StatelessElement中的mount
  newChild.mount(this, newSlot);
  assert(newChild._debugLifecycleState == _ElementLifecycle.active);
  return newChild;
}

StatelessElement中无mount方法,在其父类ComponentElement找到

@override
void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  assert(_child == null);
  assert(_active);
  _firstBuild();
  assert(_child != null);
}
  
void _firstBuild() {
  rebuild();
}

ComponentElement无rebuild,寻找其父类Element中方法

void rebuild() {
   //省略断言判断
  ...
  performRebuild();
  ...
}

再回到ComponentElement,找到其performRebuild(),此时就很明朗了

@override
void performRebuild() {
  //省略了断言和错误处理
  ...    
  Widget built;
  try {
    //调用StatelessElement中的build()方法
    built = build();
    debugWidgetBuilderValue(widget, built);
  } catch (e, stack) {
    ...
  } finally {
    ...
  }
  try {
     //遍历下去
      _child = updateChild(_child, built, slot);
      assert(_child != null);
  } catch (e, stack) {
     ...
  }
  ...
}

至此,build的流程就结束了

StatefulWidget

1. 这是什么?

StatefulWidget是一个有状态的控件,build调用的流程和上面是一样的,先StatefulElement的build方法,然后调用state中的build方法

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);

  @override
  StatefulElement createElement() => StatefulElement(this);

  @protected
  State createState();
}

2. state生命周期

state生命周期

其实也就是widget的element的创建、改变和消除的生命周期
我们按照这个图过一下state的生命周期的源码执行顺序:

class StatefulElement extends ComponentElement {
  StatefulElement(StatefulWidget widget)
    // --- 1. 构造函数创建state
    : _state = widget.createState(), super(widget) {
   //省略了断言判断
   ...
    _state._element = this;
    _state._widget = widget;
   
  }

  // --- 4. 调用build()方法,显示在树上
  @override
  Widget build() => state.build(this);

  State<StatefulWidget> get state => _state;
  State<StatefulWidget> _state;

  @override
  void _reassemble() {
    state.reassemble();
    super._reassemble();
  }

  @override
  void _firstBuild() {
    ...
    try {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
      // --- 2. 调用initState()
      final dynamic debugCheckForReturnedFuture = _state.initState() as dynamic;
      ...
    } finally {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
    }
    ...
     // --- 3. 调用didChangeDependencies()
    _state.didChangeDependencies();
    ...
    super._firstBuild();
  }

  // --- 5. 控件第二次调用自己的build方法时调用,即外部使用setState,重新绘制子树
  @override
  void update(StatefulWidget newWidget) {
    super.update(newWidget);
    final StatefulWidget oldWidget = _state._widget;
    _dirty = true;
    _state._widget = widget;
    try {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
      // --- 6. 调用didUpdateWidget()
      final dynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic;
      ...
    } finally {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
    }
    // --- 7. 执行build
    rebuild();
  }
  //build组件时调用,代表组件是活跃状态
  @override
  void activate() {
    super.activate();
    assert(_active); 
    markNeedsBuild();
  }
  //build控件时调用,代表组件是非活跃状态
  @override
  void deactivate() {
    _state.deactivate();
    super.deactivate();
  }
  //drawFrame时清空空闲控件时,若空闲则调用,解除登记
  @override
  void unmount() {
    super.unmount();
    _state.dispose();
    ...
    _state._element = null;
    _state = null;
  }

  @override
  InheritedWidget inheritFromElement(Element ancestor, { Object aspect }) {
    ...
    return super.inheritFromElement(ancestor, aspect: aspect);
  }

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

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<State<StatefulWidget>>('state', state, defaultValue: null));
  }
}

3. setState 更新状态

state进行更新,执行setState方法,这个之前分析过,这里贴个图:


setState流程.png
@protected
void setState(VoidCallback fn) {
  //去除断言
  ...
  final dynamic result = fn() as dynamic;
  _element.markNeedsBuild();
}

void markNeedsBuild() {
  ...
  if (dirty)
    return;
  _dirty = true;
  owner.scheduleBuildFor(this);
}

//对修改的脏数据进行标记和添加到列表中
void scheduleBuildFor(Element element) {
  ...
  if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
    _scheduledFlushDirtyElements = true;
    onBuildScheduled();
  }
  //添加_dirtyElements列表中
  _dirtyElements.add(element);
  //标记
  element._inDirtyList = true;
  ...
}

最后会通过c/c++回调drawFrame方法

 @override
  void drawFrame() {
    ...
    try {
      if (renderViewElement != null)
        //执行rebuild,再次调用StatefulWidget的build方法
        buildOwner.buildScope(renderViewElement);
      //绘制
      super.drawFrame();
      //释放空闲,解除登记
      buildOwner.finalizeTree();
    } finally {
      ...
    }
    ...
  }

执行rebuild方法

void buildScope(Element context, [VoidCallback callback]) {
    if (callback == null && _dirtyElements.isEmpty)
      return;
    ...
    Timeline.startSync('Build', arguments: timelineWhitelistArguments);
    try {
      _scheduledFlushDirtyElements = true;
      if (callback != null) {
        assert(_debugStateLocked);
        Element debugPreviousBuildTarget;
        ...
        _dirtyElementsNeedsResorting = false;
        try {
          callback();
        } finally {
          ...
        }
      }
      _dirtyElements.sort(Element._sort);
      _dirtyElementsNeedsResorting = false;
      int dirtyCount = _dirtyElements.length;
      int index = 0;
      while (index < dirtyCount) {
        ...
        try {
          //执行
          _dirtyElements[index].rebuild();
        } catch (e, stack) {
          ...
        }
        index += 1;
        if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
          _dirtyElements.sort(Element._sort);
          _dirtyElementsNeedsResorting = false;
          dirtyCount = _dirtyElements.length;
          while (index > 0 && _dirtyElements[index - 1].dirty) {
            index -= 1;
          }
        }
      }
     ...
    } finally {
      for (Element element in _dirtyElements) {
        assert(element._inDirtyList);
        element._inDirtyList = false;
      }
      _dirtyElements.clear();
      _scheduledFlushDirtyElements = false;
      _dirtyElementsNeedsResorting = null;
      Timeline.finishSync();
      ...
    }
    ...
  }

RenderObjectWidget

1.这是什么?

abstract class RenderObjectWidget extends Widget {
  const RenderObjectWidget({ Key key }) : super(key: key);

  @override
  RenderObjectElement createElement();

  @protected
  RenderObject createRenderObject(BuildContext context);

  @protected
  void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }

  @protected
  void didUnmountRenderObject(covariant RenderObject renderObject) { }
}

除了继承来的createElement()方法,还新增了3个方法,这次主要研究这三个方法调用的时机

RenderObjectWidget是一个抽象类,createElement()并未像之前一样实现了,但是没关系,我们可以看到其的返回类型是RenderObjectElement,这是返回通用的父类

abstract class RenderObjectElement extends Element {
  RenderObjectElement(RenderObjectWidget widget) : super(widget);

  @override
  RenderObjectWidget get widget => super.widget;

  @override
  RenderObject get renderObject => _renderObject;
  RenderObject _renderObject;

  ...

  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    // -- 1.
    _renderObject = widget.createRenderObject(this);
    assert(() { _debugUpdateRenderObjectOwner(); return true; }());
    assert(_slot == newSlot);
    attachRenderObject(newSlot);
    _dirty = false;
  }

  @override
  void update(covariant RenderObjectWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    assert(() { _debugUpdateRenderObjectOwner(); return true; }());
    // -- 2.
    widget.updateRenderObject(this, renderObject);
    _dirty = false;
  }

  @override
  void performRebuild() {
    // -- 2.
    widget.updateRenderObject(this, renderObject);
    _dirty = false;
  }

  ...
 
  @override
  void unmount() {
    super.unmount();
    //省略断言
    ...
    // -- 3.
    widget.didUnmountRenderObject(renderObject);
  }

  ...
}

经过前面的分析 ,其实本身比较简单,复杂的部分都在RenderObject上,如何摆放和绘制


针对ProxyWidget放在下一篇:深入分析Widget 2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280

推荐阅读更多精彩内容

  • 本文主要介绍了Flutter布局相关的内容,对相关知识点进行了梳理,并从实际例子触发,进一步讲解该如何去进行布局。...
    Q吹个大气球Q阅读 9,596评论 6 51
  • 原文在此,此处只为学习 Widget与ElementWidget主要接口Stateless WidgetState...
    lltree阅读 4,459评论 0 1
  • 原文链接:链接 概要 本文不是flutter界面开发入门文章,而是一篇深入介绍Flutter framework关...
    盖世英雄_ix4n04阅读 8,677评论 0 9
  • Flutter是什么 Google开源的,基于skia(开源,C/C++)绘图引擎,支持OpenGL,metal,...
    念_夕阅读 4,396评论 0 4
  • 吃了 喝了 睡了 我把欲望绞碎了 烧了 驾驭的是我 你是我的心魔 如果 行走是一种附着 贯穿的都是生活 穿梭
    shinesshadow阅读 164评论 0 0