美文网首页
手撕flutter(一)Widget

手撕flutter(一)Widget

作者: ZoranLee | 来源:发表于2021-02-23 18:00 被阅读0次

    Widget 与 Element

    image.png

    Widget 粗略源码

    @immutable
    abstract class Widget extends DiagnosticableTree {
      const Widget({ this.key });
      final Key key;
    
      @protected
      Element createElement();
    
      @override
      String toStringShort() {
        return key == null ? '$runtimeType' : '$runtimeType-$key';
      }
    
      @override
      void debugFillProperties(DiagnosticPropertiesBuilder properties) {
        super.debugFillProperties(properties);
        properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
      }
    
      static bool canUpdate(Widget oldWidget, Widget newWidget) {
        return oldWidget.runtimeType == newWidget.runtimeType
            && oldWidget.key == newWidget.key;
      }
    }
    
    
    • Widget类继承自DiagnosticableTree,DiagnosticableTree即“诊断树”,主要作用是提供调试信息。

    • Key: 这个key属性类似于React/Vue中的key,主要的作用是决定是否在下一次build时复用旧的widget,决定的条件在canUpdate()方法中。

    • createElement():正如前文所述“一个Widget可以对应多个Element”;Flutter Framework在构建UI树时,会先调用此方法生成对应节点的Element对象。此方法是Flutter Framework隐式调用的,在我们开发过程中基本不会调用到。

    • debugFillProperties(...) 复写父类的方法,主要是设置诊断树的一些特性。

    • canUpdate(...)是一个静态方法,它主要用于在Widget树重新build时复用旧的widget,其实具体来说,应该是:是否用新的Widget对象去更新旧UI树上所对应的Element对象的配置;通过其源码我们可以看到,只要newWidget与oldWidget的runtimeType和key同时相等时就会用newWidget去更新Element对象的配置,否则就会创建新的Element。

    • Widget类本身是一个抽象类,其中最核心的就是定义了createElement()接口

    • 我们一般都不用直接继承Widget类来实现一个新组件,相反,我们通常会通过继承StatelessWidget或StatefulWidget来间接继承Widget类来实现。

    StatelessWidget

    继承自Widget类,重写了createElement()方法:

    @override
    StatelessElement createElement() => new StatelessElement(this);
    
    • StatelessElement 间接继承自Element类,与StatelessWidget相对应(作为其配置数据)。
    • StatelessWidget用于不需要维护状态的场景,它通常在build方法中通过嵌套其它Widget来构建UI,在构建过程中会递归的构建其嵌套的Widget。
    class Echo extends StatelessWidget {
      const Echo({
        Key key,  
        @required this.text,
        this.backgroundColor:Colors.grey,
      }):super(key:key);
    
      final String text;
      final Color backgroundColor;
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Container(
            color: backgroundColor,
            child: Text(text),
          ),
        );
      }
    }
    

    使用

    Widget build(BuildContext context) {
      return Echo(text: "hello world");
    }
    

    Context

    build方法有一个context参数,它是BuildContext类的一个实例,表示当前widget在widget树中的上下文,每一个widget都会对应一个context对象(因为每一个widget都是widget树上的一个节点)。实际上,context是当前widget在widget树中位置中执行”相关操作“的一个句柄,比如它提供了从当前widget开始向上遍历widget树以及按照widget类型查找父级widget的方法。下面是在子树中获取父级widget的一个示例:

    class ContextRoute extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Context测试"),
          ),
          body: Container(
            child: Builder(builder: (context) {
              // 在Widget树中向上查找最近的父级`Scaffold` widget
              Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
              // 直接返回 AppBar的title, 此处实际上是Text("Context测试")
              return (scaffold.appBar as AppBar).title;
            }),
          ),
        );
      }
    }
    

    StatefulWidget

    和StatelessWidget一样,StatefulWidget也是继承自Widget类,并重写了createElement()方法,不同的是返回的Element 对象并不相同;另外StatefulWidget类中添加了一个新的接口createState()。

    • StatefulWidget的类定义
    abstract class StatefulWidget extends Widget {
      const StatefulWidget({ Key key }) : super(key: key);
    
      @override
      StatefulElement createElement() => new StatefulElement(this);
    
      @protected
      State createState();
    }
    
    • StatefulElement 间接继承自Element类,与StatefulWidget相对应(作为其配置数据)。StatefulElement中可能会多次调用createState()来创建状态(State)对象。
    • createState() 用于创建和Stateful widget相关的状态,它在Stateful widget的生命周期中可能会被多次调用。

    State

    • 一个StatefulWidget类会对应一个State类
      State中的保存的状态信息可以:

    • 在widget 构建时可以被同步读取。

    • 在widget生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter framework状态发生改变,Flutter framework在收到消息后,会重新调用其build方法重新构建widget树,从而达到更新UI的目的。

    class CounterWidget extends StatefulWidget {
      const CounterWidget({
        Key key,
        this.initValue: 0
      });
    
      final int initValue;
    
      @override
      _CounterWidgetState createState() => new _CounterWidgetState();
    }
    
    CounterWidget接收一个initValue整型参数,它表示计数器的初始值。下面我们看一下State的代码:
    
    class _CounterWidgetState extends State<CounterWidget> {  
      int _counter;
    
      @override
      void initState() {
        super.initState();
        //初始化状态  
        _counter=widget.initValue;
        print("initState");
      }
    
      @override
      Widget build(BuildContext context) {
        print("build");
        return Scaffold(
          body: Center(
            child: FlatButton(
              child: Text('$_counter'),
              //点击后计数器自增
              onPressed:()=>setState(()=> ++_counter,
              ),
            ),
          ),
        );
      }
    
      @override
      void didUpdateWidget(CounterWidget oldWidget) {
        super.didUpdateWidget(oldWidget);
        print("didUpdateWidget");
      }
    
      @override
      void deactivate() {
        super.deactivate();
        print("deactive");
      }
    
      @override
      void dispose() {
        super.dispose();
        print("dispose");
      }
    
      @override
      void reassemble() {
        super.reassemble();
        print("reassemble");
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print("didChangeDependencies");
      }
    
    }
    
    

    相关文章

      网友评论

          本文标题:手撕flutter(一)Widget

          本文链接:https://www.haomeiwen.com/subject/bjarfltx.html