美文网首页
Flutter中的Widget

Flutter中的Widget

作者: iDevOps | 来源:发表于2019-08-21 16:48 被阅读0次
    Widget简介

    在Flutter中, 几乎所有的对象都是一个Widget, 它不仅可以表示UI元素, 也可以表示一些功能性的组件, 如手势检测的GestureDetector、App主题数据传递的Theme等等, 而在原生开发中, 组件通常指的是UI组件。

    Widget与Element

    官方文档中, Widget的功能是描述一个UI元素的配置数据, 也就是说, Widget只是显示元素的配置数据, 并不是表示手机屏幕的显示元素。
    Element才是真正代表屏幕上的显示元素, Widget只是UI元素的配置数据, 一个Widget可以对应多个Element。
    Widget是一个抽象类, 其中最核心的就是定义了createElement()接口, 在Flutter开发中,我们一般都不用直接继承Widget类来实现一个新组件,相反,我们通常会通过继承StatelessWidget或StatefulWidget来间接继承Widget类来实现。StatelessWidget和StatefulWidget都是直接继承自Widget类,而这两个类也正是Flutter中非常重要的两个抽象类,它们引入了两种Widget模型,接下来我们将重点介绍一下这两个类。

    StatelessWidget
    1. 继承自Widget类,重写了createElement()方法
    2. 用于不需要维护状态的场景
    3. 在build方法中通过嵌套其它Widget来构建UI,在构建过程中会递归的构建其嵌套的Widget

    实现一个回显字符串的Echo widget

    import 'package:flutter/material.dart';
    
    //main是入口函数
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("Hello Flutter title"),
            ),
            body: new Center(
              child: Echo(text: 'Echo Flutter')
            ),
          ),
        );
      }
    }
    
    class Echo extends StatelessWidget {
      
      //属性, Widget的属性应尽可能的被声明为final,防止被意外改变。
      final String text;
      final Color backgroundColor;
    
      //构造方法
      const Echo({
        Key key,//这个key属性类似于React/Vue中的key,主要的作用是决定是否在下一次build时复用旧的widget,决定的条件在Widget类中的canUpdate()方法中, 不理解就记住继承Widget时, 第一个参数通常应该是key
        @required this.text, //@required标注代表必须要传的参数
        this.backgroundColor:Colors.grey,
      }):super(key:key);
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Container(
            color: backgroundColor,
            child: Text(text),
          ),
        );
      }
    }
    

    运行效果:


    回显字符串
    • Context

    build方法有一个context参数,它是BuildContext类的一个实例, 表示当前widget在widget树中的上下文,一个widget都会对应一个context对象(因为每一个widget都是widget树上的一个节点)。
    Context怎么用?
    可以用context向上遍历widget树或按照widget类型查找父级widget

    import 'package:flutter/material.dart';
    
    //main是入口函数
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("Hello Flutter title"),
            ),
            body: Container(
              child: Builder(builder: (context){
                //在Widget树中向上查找最近的父级Scaffold
                Scaffold scaffold = context.ancestorWidgetOfExactType(Scaffold);
                //返回AppBar的title
                return (scaffold.appBar as AppBar).title;
              }),
            )
          ),
        );
      }
    }
    

    运行效果

    Context的用法
    StatefulWidget

    1.和StatelessWidget一样, StatefuleWidget也继承了Widget类, 并重写了createElement()方法
    2.和StatelessWidget不一样的地方就是, 返回的Element对象不同, StatefulWidget类中添加了一个新的接口createState()
    3.createState用于创建和StatefuleWidget相关的状态, 在StatefuleWidget整个生命周期中会被多次调用。

    State

    1.一个StatefuleWidget类会对应一个State类, State类表示与其对应的StatefulWidget要维护的状态
    2.StatefuleWidget创建时可以读取State的状态
    3.在StatefuleWidget生命周期中, 可以通过手动调用setState()方法通知Flutter framework状态发生改变, Flutter framework收到消息后, 会重新调用其build方法重新构建widget树, 从而达到更新UI的目的

    • State有两个重要的属性, widget和context
      1.widget表示与该State实例关联的widget实例, 当时这种关联并非是永久的, State实例只会在第一次插入到树中时被创建, 当重新构建时, 如果widget被修改了, Flutter framework会动态设置State.widget为新的实例;
      2.context是StatefuleWidget对应的BuildContext, 作用跟上面讲到的StatelessWidget的BuildContext一致。
    • State的生命周期


      State生命周期
    class CounterWidget extends StatefulWidget{
    
      final int initValue;
    
      const CounterWidget({
        Key key,
        this.initValue: 0, //初始值为0
      });
    
      @override
      State<StatefulWidget> createState() {
        return _CounterWidgetState();
      }
    }
    
    class _CounterWidgetState extends State<CounterWidget>{
    
      int _counter;
    
      /*
       * 1. 当Widget第一次插入到Widget树时会被调用, 对于每一个State, Flutter framework只会调用一次该方法
       * 2. 通常我们在这里做一些一次性的操作, 如状态初始化、时间通知等
       * 3. 不能在该回调中调用BuildContext.inheritFromWidgetOfExactType, 
       *    原因是在初始化完成后,Widget树中的InheritFromWidget也可能会发生变化,
       *    所以正确的做法应该在在build()方法或didChangeDependencies()中调用它
       */
      @override
      void initState() {
        super.initState();
        _counter = widget.initValue;
        print("initState");
      }
    
      /*
       * 1. 当State对象的依赖发生变化时会被调用
       */
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print("didChangeDependencies");
      }
    
      /*
       * 1. 主要用于构建Widget子树
       * 2. 一般在调用initState()或didUpdateWidget()或setState()或didChangeDependencies()后会被调用
       * 3. 在State对象从树中的一个位置移除, 又重新调插入到树的其他位置后也会被调用
       */
      @override
      Widget build(BuildContext context) {
        print("build");
        return Scaffold(
          body: Center(
            child: FlatButton(
              child: Text('$_counter'),
              //点击数字++
              onPressed: ()=>{
                setState(()=>++_counter)
              },
            ),
          ),
        );
      }
    
      /*
       * 1. 专门为调试提供的, 热重载后会贝调用, 在Release模式下永远不会被调用 
       */
      @override
      void reassemble() {
        super.reassemble();
        print("reassemble");
      }
    
      /*
       *  1. 在Widget重新构建时, Flutter framework会调用Widget.canUpdate来检测Widget树中同一位置的新旧节点,
       *     然后决定是否需要更新, 如果widget.canUpdate()返回true则会调用此回调 
       */
      @override
      void didUpdateWidget(CounterWidget oldWidget) {
        super.didUpdateWidget(oldWidget);
        print("didUpdateWidget");
      }
    
      /*
       * 1. 当State对象从树中移除时, 会被调用
       * 2. 在一些场景下, Flutter framework会将对象重新插入树中, 如果移除后没有重新插入, 会调用dispose方法 
       */
      @override
      void deactivate() {
        super.deactivate();
        print("deactivate");
      }
    
      /*
       * 1. State对象从树中被永久移除时调用, 通常在此回调中释放资源 
       */
      @override
      void dispose() {
        super.dispose();
        print("dispose");
      }
    }
    
    • 在Widget树中获取State对象
      由于StatefuleWidget的具体逻辑都在State中, 所以很多时候, 我们需要获取StatefulWidget对应的State对象来调用一些方法, 有两种方法可以获取。

    1.通过Context获取
    context对象有一个ancestorStateOfType(TypeMatcher)方法,该方法可以从当前节点沿着widget树向上查找指定类型的StatefulWidget对应的State对象。

    import 'package:flutter/material.dart';
    
    //main是入口函数
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: new AppBar(
              title: new Text("子树中获取State对象"),
            ),
            body: Center(
              child: Builder(builder: (context){
                return RaisedButton(
                  onPressed: (){
                    ScaffoldState _state = context.ancestorStateOfType(TypeMatcher<ScaffoldState>());
                    _state.showSnackBar(
                      SnackBar(content: Text("我是SnackBar"),),
                    );
                  },
                  child: Text("显示SnackBar"),
                );
              })
            ),
          ),
        );
      }
    }
    

    运行效果


    SnackBar效果
    1. 通过GlobalKey获取State

    分两步
    第一步, 先给目标StatefuleWidget添加GlobalKey,

    //定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
    static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
    ...
    Scaffold(
        key: _globalKey , //设置key
        ...  
    )
    

    第二步, 通过GlobalKey获取State对象

    _globalKey.currentState.openDrawer()
    

    GlobalKey是Flutter提供的一种在整个APP中引用element的机制
    如果一个Widget设置了GlobalKey
    那么我们便可以通过globalKey.currentWidget获取Widget对象
    通过globalKey.currentElement来获取Widget对应的element对象,
    如果是StatefuleWidget, 则可以通过globalKey.currentState来获取该widget对象的State对象

    注: 使用GlobalKey开销较大,如果有其他可选方案,应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的,不能重复。

    相关文章

      网友评论

          本文标题:Flutter中的Widget

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