flutter源码:widget是如何被加载的

2022-04-11 17:51:49 浏览数 (1)

从flutter的入口main方法开始,一步步看下widget是如何被加载的

在Flutter中,一切皆widget,我们有两大widget,statelessWidget和stetefulWidge,会分别看两种下widget是如何被加载出来的,展示的源码会有删减,仅展示跟主题有关的代码

入口到加载

flutter的入口,就是runApp方法,我们也从这个方法开始查看

代码语言:javascript复制
void main() {
  runApp(const MyApp());
}

进入runApp方法,外部传的widget传给了scheduleAttachRootWidget方法

代码语言:javascript复制
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

其中的..叫做级联运算符,表示对第一个对象的操作,而忽略每个方法的返回值,上面的方法,等同于下面的方法

代码语言:javascript复制
void runApp(Widget app){
  final binding = WidgetsFlutterBinding.ensureInitialized()
  binding.scheduleAttachRootWidget(app);
  binding..scheduleWarmUpFrame();
}

..会让写法更简洁,更优雅

继续往下看scheduleAttachRootWidget方法,我会删除一些无关的代码

代码语言:javascript复制
  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
  }
代码语言:javascript复制
void attachRootWidget(Widget rootWidget) {                                                  
  _readyToProduceFrames = true;                                                                   
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(                                    
    container: renderView,                                                                        
    debugShortDescription: '[root]',                                                              
    child: rootWidget,                                                                            
  ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);                                      
}                                                                                                 

然后调用到attachToRenderTree

代码语言:javascript复制
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
  if (element == null) {                                                                                                               
      element = createElement();                                                                                                  
      element!.assignOwner(owner);                                                                                
    });                                                                                                           
    owner.buildScope(element!, () {                                                                               
      element!.mount(null, null);                                                                                 
    });                                                                                                           
  } else {                                                                       
  }                                                                                                               
  return element!;                                                                                                
}                                                                                                                 

上面的代码很关键,这里生成的全局第一个element,继续看下createElement()方法

代码语言:javascript复制
RenderObjectToWidgetElement<T> createElement() => 
RenderObjectToWidgetElement<T>(this);    

这里可以知道,全局的root element是RenderObjectToWidgetElement,这个是系统内部的element,接下来,调用了它的mount方法

代码语言:javascript复制
void mount(Element? parent, Object? newSlot) { 
  super.mount(parent, newSlot);                 
  _rebuild();                          
}                                               
代码语言:javascript复制
void _rebuild() {                                                
  try {                                                          
    _child = updateChild(_child, widget.child, _rootChildSlot);  
  } catch (exception, stack) {                                   
  }                                                              
}                                                                

然后调用了element的updateChild方法,这个方法是一个核心方法,目的是新建或者更新这个element的child element,到这里,我们自己写的传给系统最外层的widget也是在这里被加载的

代码语言:javascript复制
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
  if (newWidget == null) {
    if (child != null) deactivateChild(child);
    return null;
  }

  final Element newChild;
  if (child != null) {
  } else {
    newChild = inflateWidget(newWidget, newSlot);
  }
  return newChild;
}

一开始,child是null,newWidget就是我们传的顶层Widget,然后走到inflateWidget方法,这里实际加载element

代码语言:javascript复制
Element inflateWidget(Widget newWidget, Object? newSlot) {
  assert(newWidget != null);
  final Element newChild = newWidget.createElement();
  newChild.mount(this, newSlot);
  return newChild;
}

先是调用newWidget.createElement()来生成element,然后再调用element的mount方法,这里结合widget源码来看下

代码语言:javascript复制
abstract class StatelessWidget extends Widget {
  const StatelessWidget({ Key? key }) : super(key: key);

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

  @protected
  Widget build(BuildContext context);
}

statelessWidget是生成StatelessElement

代码语言:javascript复制
abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key? key }) : super(key: key);
  
  @override
  StatefulElement createElement() => StatefulElement(this);
  
  @protected
  @factory
  State createState(); // ignore: no_logic_in_create_state, this is the original sin
}

statefulWidget是生成StatefulElement,它们的继承关系是这样

代码语言:javascript复制
StatefulElement -> ComponentElement -> Element
StatelessElement -> ComponentElement -> Element

然后再看下它的mount方法

代码语言:javascript复制
void mount(Element? parent, Object? newSlot) {         
  super.mount(parent, newSlot);     
  _firstBuild();                              
}                   

void _firstBuild() {  
  rebuild(); 
}                                                                     

这里的_firstBuild如果是statefulWidget,会走到它state的两个关键的生命周期调用

代码语言:javascript复制
void _firstBuild() {
  try {
    final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
  } finally {
  }
  state.didChangeDependencies();
  super._firstBuild();
}

先调用state的initState,所以可以知道,state的第一个调用的生命周期方法就是这个,然后继续调用state的didChangeDependencies方法,然后调用到element的rebuild方法

代码语言:javascript复制
void rebuild() {                                                                                     
  performRebuild();                                                                                
}    
void performRebuild();        

element的performRebuild方法是空实现,最终又回到了ComponentElement来实现

代码语言:javascript复制
void performRebuild() {
  Widget? built;
  try {
    built = build();
  } catch (e, stack) {
  } finally {
  }
  
  try {
    _child = updateChild(_child, built, slot);
  } catch (e, stack) {
  }
}

这里有两个重要的方法,先是调用了build(),这里就是会最终调用到widget的build方法,就是我们每次实现widget都要实现的方法,然后又调用updateChild方法,继续加载这个widget的子widget,一直循环下去,直到全部加载完

代码语言:javascript复制
class StatelessElement extends ComponentElement {                        
  StatelessElement(StatelessWidget widget) : super(widget);              
                                                                         
  @override                                                              
  StatelessWidget get widget => super.widget as StatelessWidget;         
                                                                         
  @override                                                              
  Widget build() => widget.build(this);                                                           
}                                                                        

对于statelessWidget,就直接调用build方法,加载这个widget,再看下statefulWidget

代码语言:javascript复制
class StatefulElement extends ComponentElement {
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
  }

  @override
  Widget build() => state.build(this);
  
  }

state.build方法,这里就是实现statefulWidget必须实现的build方法

生命周期

通过上面的代码,可以知道statelessWidget的生命周期如下

代码语言:javascript复制
1、createElement
2、build

statelessWidget是一旦生成,就不会变,生命周期也比较简单,再看下statefulWidget

代码语言:javascript复制
1、createElement
2、createState
3、initState
4、didChangeDependencies
5、build

这里的生命周期是只到加载出来,后续还有更新、销毁等,这里先不提

总结

1、widget的所有方法,都是在同个线程按照从外层到内层逐级往里调用,也就是主线程,dart中叫main isolate 2、如果在widget中,有耗时的方法,应该放在异步执行,可以使用compute,或者isolate提供的异步方法 3、widget的目的,其实是为了生成对应的element,也就是widget树是为了生成对应的element树

0 人点赞