Flutter - Widget-Context-Stage

读前须知:此篇文章基本上是Widget - State - Context - InheritedWidget的翻译而且删减了部分我我的以为没有意义的文字,保留下来的部分也不会逐字逐句精确翻译,因此其实强烈推荐阅读英文原文。app

如下文章里面除了第一张思惟导图是本人所做以外,凡是出现的示例代码和图片都是上面提到的英文文章里面的。本人在这里对此表示感激和愧疚,若是涉及到侵权,本人会当即删掉整篇文章。less

All the example codes and images used in this article are from Widget - State - Context - InheritedWidget excepting the first one. If this is of tort, I will delete this article immediately.ide

正文开始:函数

Widget, State 和Context是每个flutter开发者必需要彻底理解的概念,可是具体来讲要理解哪些知识呢?这篇文章会就如下几个知识点进行讲解:布局

1: Stateful和Stateless widget的差异动画

2:什么是Contextui

3:  什么是State以及怎么使用this

4:一个context和他的state之间的关系spa

5:InheritedWidget以及在在Widget树里面怎么传递信息翻译

6:rebuild的概念

接下来文章会按照如下结构去展开:

image

PS:因为此篇的篇幅很长,此思惟导图里面的第二部分(左边的’怎样获取State‘)的篇幅也会很长,因此第二部分会放到下一篇文章讲解。

一:基本概念解释

1:什么是Widget

Widget即组件。在flutter里面几乎万物都是Widget,跟咱们常说的component是同一个概念。

2: 什么是Widget tree

Widget按照树形结构组合的产物就是Widget tree。这个Widget tree的每一个节点上又是一个Widget。包含其余组件的组件叫作父组件,被其余组件包含的组件叫作子组件。好比下面一段代码:

@override
Widget build(BuildContext){
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
}

上面的一段代码,若是咱们用图像表示它的widget tree的话就以下图所示:
图片描述

3:什么是Context
一个context标识了一个widget在widget tree的结构中是在哪一个地方被build的。简而言之就是一个context标明了一个widget是挂载在widget tree的那个节点的。

一个context只属于一个widget。
假如一个widget A有子组件,那么widget A的context会变成子组件的父context。

以上文提到的widget tree为例子,假如咱们画出它的context,以下图所示(一个颜色表明一个context):
图片描述

Context Visibility(上下文可见性)

只在其本身的context或者在其父context可见。
从以上描述,咱们能够轻易地找到一个widget的父widget,例如:

Scaffold > Center > Column > Text:
context.ancestorWidgetOfExtractType(Scaffold) => 会找到第一个沿着Text的context一路向上而遇到的第一个Scaffold.

从一个父组件,也能找到子组件,可是不建议这么作(稍后会讨论到)。

4:Widget的分类
在Flutter里面有2类widget:

  • Stateless Widget
  • Stateful Widget

从字面意思上能够理解Stateless Widget就是无状态的组件,Stateful Widget是有状态的组件,接下来咱们对两者作更具体的讲解。
Stateless Widget
有些组件只依赖于他们本身的配置信息,这些配置信息通常是由他们的父组件在build他们的时候提供。
换句话说,这些组件一旦建立以后,就不用担忧任何变化。
这类型的组件就是Stateless Widget.
典型的例子好比Text, Row, Column, Container等,对于这些组件来讲在build的时候,咱们只须要传一些参数给他们就好。
一个Stateless Widget只能被build一次,意思就是一旦build,不会由于任何的事件或者用户行为而从新build。

Stateless Widget lifecycle
下面是一个典型的Stateless Widget的代码结构。
如咱们所见,咱们传递一些阐述给它的构造函数,可是请记住,这些参数不会再以后被改变了,因此通常你也会看到这些参数是被定义为final的。

class MyStatelessWidget extends StatelessWidget {

    MyStatelessWidget({
        Key key,
        this.parameter,
    }): super(key:key);

    final parameter;

    @override
    Widget build(BuildContext context){
        return new ...
    }
}

虽然有另外一个方法(createElement)能够被overridden,可是通常没人会用到它。惟一一个须要被override的方法就是build().
Stateless widget的生命周期是直接而简单的,以下所示:

  • 初始化
  • 经过build()方法渲染

Stateful Widget

一些其余的组件所拥有的数据会在组件生命周期内产生变化,这些数据就变成了dynamic(动态的)的。

这些被组件拥有的会在组件的生命周期内改变的数据的列表,咱们叫作State

而拥有以上特色的组件,咱们就叫作Stateful Widget

Stateful Widget的例子就比如一个用户能够选择的Checkboxes的列表或者一个会根据某种条件而disabled的Button。

5: 什么是State
一个State定义了一个StatefulWidget的“行为”部分。
State包含了如下旨在与一个组件交互的:

  • 行为(behaviour)
  • UI布局(layout)

任何对State的改变都会致使这个组件的rebuild。

6:State和Context之间的关系
对Stateful Widgets而言,一个State是和一个Context是绑定的,并且这种绑定关系是永久的,一个State永远不会改变他的Context。

即便一个组件的Context在Widget tree上发生了移动,这个State仍是会依然和那个Context绑定在一块儿。

很是重要的知识点:
由于一个State对象和一个Context是绑定的,这就意味着这个State对象不能从其余的Context下直接被获取到(这一点以后会讨论到)

7:StatefulWidget的生命周期(lifecycle)
前面已经介绍和不少StatefulWidget的相关概念和基础知识,接下来咱们来了解一下StatefulWidget的生命周期,这里不会介绍所有的生命周期,先只挑几个重要的,与本篇文章的主旨相关的几个来说。先看下下面一个StatefulWidget的例子:

class MyStatefulWidget extends StatefulWidget {

    MyStatefulWidget({
        Key key,
        this.parameter,
    }): super(key: key);
    
    final parameter;
    
    @override
    _MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

    @override
    void initState(){
        super.initState();
        
        // Additional initialization of the State
    }
    
    @override
    void didChangeDependencies(){
        super.didChangeDependencies();
        
        // Additional code
    }
    
    @override
    void dispose(){
        // Additional disposal code
        
        super.dispose();
    }
    
    @override
    Widget build(BuildContext context){
        return new ...
    }
}

下面的这个图展现了(一个简化的版本)一个StatefuleWidget在建立的时候,内部的一些列行为。在这个图的右边你能够看到一个State对象的内部状态变化以及最右边的Context是在何时和State产出联系而所以变为可用的。
图片描述

initState()
initState()是在构造函数以后的第一个被调用的生命周期方法。当你须要再初始化阶段作一个额外的操做的时候,你须要override它。典型的在initState()方法里额外的操做好比动画,或者某些数据准备。假如你override initState(),记得调用super.initState()而且这行代码要放在initState()方法体的最前面。意思就是你得让super.initState()执行了以后再去执行你额外的初始化工做。

在这个阶段,一个context是存在的,可是你并不能真正地使用它,由于这时候context和state尚未完成绑定。

一旦initState()执行完毕,State对象就初始化好了,context也就能够被使用了。

initState()在整个生命周期内只会被调用一次

didChangeDependencies()
didChangeDependencies()是在生命周期里第二个被调用的方法。

这个阶段,context已经能够被使用了。

假如你的组件是连接到了InheritedWidget,根据context你须要初始化一些listeners(监听者),一般你须要override这个方法。

注意,若是某个组件是连接了InheritedWidget,那么这个组件每次重建(rebuild),didChangeDependencies()都会被调用。

假如你要override这个方法,你应该首先调用super.didChangeDependencies().

build()
build()跟在didChangeDependencies()(和didUpdateWidget())以后被调用。

这个方法就是用来构建你的组件的。

每一次State对象发生变化(或者InheritedWidget须要通知它的注册者)时,build()方法都会被调用。

一般,咱们经过调用setState((){…})来改变State对象,强制build()被调用,从而从新build咱们的widget。

dispose()
dispose()在这个组件被销毁的时候被调用。

通常咱们override这个方法,能够在组件被销毁的时候作一个清理工做。

override dispose()记得调用super.dispose()而且把它放在方法体的最后

8: StatelessWidget和StatefulWidget之间的抉择
既然在Flutter里面有StatelessWidget和StatefulWidget这两种类型的组件,那在这两者之间如何抉择呢?

记住标准就是:
在这个组件的生命周期内,是否有会变化的数据,这个组件是否须要rebuild?

若是答案是yes,那你就须要一个StatefulWidget而不会StatelessWidget。

9:StatefulWidget的2个组成部分

组件的构造函数部分

class MyStatefulWidget extends StatefulWidget {
    MyStatefulWidget({
        Key key,
        this.color,
    }): super(key: key);
    
    final Color color;

    @override
    _MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

这部分是一个StatefulWidget的public部分。这部分不会在一个组件的生命周期内发生改变,它只是接收一些参数以便它的State可使用。好比上面这个例子里面的color这个参数。

Widget State定义部分

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
    ...
    @override
    Widget build(BuildContext context){
        ...
    }
}

_MyStatefulWidgetState是这个Widget在其生命周期内变化的部分,也是使得这个Widget可以rebuild的部分。
_MyStatefulWidgetState经过widget.{name of the variable}能够获取存在MyStatefulWidget内的任意变量。例如这里,能够经过widget.color获取color变量。

10:Widget的惟一标识-key
在Flutter里面,每个组件都是惟一标识的,这个惟一标识是在build的时候被定义的。

这个惟一的标识就是组件的可选参数:Key. 加入key缺省了,系统会默认给你建立一个。

在某些情形下,你必须强制制定key,以便你能够经过这个key获取到这个组件。

你能够经过下面的一些helper来达到上面的目的:GlobalKey, LocalKey, UniqueKey或者ObjectKey。

GlobalKey保证这个key在整个application里面都是惟一的。

如下的例子就是保证myKey在整个application都是惟一的:

GlobalKey myKey = new GlobalKey();
    ...
    @override
    Widget build(BuildContext context){
        return new MyWidget(
            key: myKey
        );
    }
PS:因为此篇的篇幅已经够长,以前的思惟导图里面的第二部分的篇幅也会很长,因此第二部分会放到下一篇文章讲解。
相关文章
相关标签/搜索