前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >flutter源码:widget是如何被加载的

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

作者头像
韦东锏
发布2022-04-11 17:51:49
6680
发布2022-04-11 17:51:49
举报
文章被收录于专栏:Android码农

从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树

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android码农 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 入口到加载
  • 生命周期
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档