Flutter(八)——状态管理

前言

在学习完Flutter的常用组件之后,我们对这些组件的使用都有了一定的了解。但是,只是了解还是不够的,因为我们的数据是动态的,具备交互性,界面上的展示会根据用户的操作产生变化。这个时候,就需要对Flutter的状态管理机制有一定的了解,我们先来看一张图:
首图Flutter一切皆是组件,而组件Widget主要被划分为StatelessWidget和StatefulWidget两大类,下面将分辨介绍一下两类组件。

Widget

我们使用Flutter开发过程中,会出现布局层级。不管这个界面是简单的,还是复杂的,都会有布局的嵌套层级,这样的布局我们称为Widget树,并且在Widget之间存在这“父子”关系。其实Widget只是起到“描述”的作用,真正在屏幕上显示给用户看的UI是Element,Widget只是描述了Element的数据配置,Widget树的层级展现形式如下:
Widget如果一个Widget存在子Widget,那么上一层的Widget就是父Widget,父Widget里面的则是子Widget。在实际开发中,我们会遇到复杂的布局情况,这个时候,不妨记住这颗布局树,回想一下,就会清晰了。

Context

做过Java开发的,应该清楚这个Context,因为用的最多,就是上下文的意思。Context对应的是,构建Widget树结构中具体某一个Widget的位置引用,并且它被视为Widget一部分,而每个Context只对应一个Widget。如果一个Context对应的是父Widget A,则Widget A对应的Context也包含了子Widget的Context。通过Context可以遍历和查找当前Widget树,Context之间也是关联在一起的,它们组成一颗Context树。

StatelessWidget

StatelessWidget即无状态的Widget,它无法通过setState设置组件的状态,其写法通过继承StatelessWidget,然后重写build函数来实现。对于其内部属性,应该声明为final,以防止被意外改变,例如:

class MyStatelessWidget extends StatelessWidget{
	MyStatelessWidget({
		Key key;
		this.parameter,
	}):super(key:key);
	final parameter;
	@override
	Widget build(BuildContext context){
		return new ....
	}
}

上面的代码中parameter参数只能传递一次,之后就无法被修改了。它的生命周期非常简单,就两部:1.初始化,2.通过build进行渲染。

StatefulWidget

StatefulWidget是有状态的Widget。当我们创建一个StatefulWidget组件时,它同时也创建了一个State对象,并且StatefulWidget通过与State关联可以达到刷新UI的目的。我们看一下官方的一个图示:
UI和React/Vue中的Reactive思想相似,但是还是有区别的,StatefulWidget只需要调用setState(…)方法,而不需要调用类似Android里的textView.setText(…)方法。某些组件在其生命周期里,内部数据会发生变化,基于这种情况,我们应该考虑使用StatefulWidget。

StatefulWidget的组成

StatefulWidget由两部分组成,第一部分为主体部分,代码如下所示:

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

在主体部分里Widget继承了StatefulWidget的内容。在主体部分中创建的变量是无法变更的,第二部分代码如下:

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

MyHomePageState前面以""开头,证明这个类为私有,可以通过widget.{name of the variable}格式访问第一部分里定义的变量,比如widget.title。

State

State是对StatefulWidget的行为和布局的描述,和StatefulWidget存在一一对应的关系。有时候StatefulWidget的组件需要改变其界面的表现形式,这个时候就可以通过setState(…)来改变。Flutter为我们做的服务功能非常全面,改变的过程只需要由Framework层控制,从而达到更新UI的目的。

State生命周期

在学习Java开发Android时,我们需要学习各种Activity,Framgent的生命周期概念。Flutter也一样,Framework层为StatefulWidget对应的State定制了生命周期的回调方法,如下图所示:
生命周期那么,这些回调方法分别时在什么时候调用的呢?这部分内容博主建议不需要死记硬背,当然你一目十行当我没说,博主这里将通过例子并结合上图来说明。

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void initState(){
    super.initState();
    print("initState");
  }

  void didChangeDependencies(){
    super.didChangeDependencies();
    print("didChangeDependencies");
  }

  void reassemble(){
    super.reassemble();//热重载回调,release模式下不起作用
    print("reassemble");
  }

  @override
  void didUpdateWidget(MyHomePage oldWidget){
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget");
  }

  @override
  void deactivate(){
    super.deactivate();
    print("deactivate");
  }
  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    print("dispose");
  }


  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    print("build");
    return Scaffold(
      appBar: AppBar(
        title: Text("生命周期"),
        actions: <Widget>[
          new IconButton(
              icon: new Icon(
                Icons.navigate_next,
                color: Colors.white,
              ),
              onPressed: (){
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context)=>NextPageWidget()
                    )
                );
          }),

        ],
      ),
      body: Center(
        child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: <Widget>[
           Text("点击下面按钮进行自加操作"),
           Text("$_counter",style: Theme.of(context).textTheme.display1,),
         ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: "自加",
        child: Icon(Icons.add),
      ),
    );
  }
class NextPageWidget extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(title:Text( "跳转页面"),),
      body: Center(
        child: Text("这个是第二页"),
      ),
    );
  }
}

执行这段代码后,也就是我们刚刚打开App的时候,我们看一下控制台输出如下内容:
在这里插入图片描述
由此可知,首次启动界面执行的顺序是:initState->didChangeDependencies->build。接着我们点击一下热重载,也就是Android Studio右上角闪电图标,控制台输出如下:
在这里插入图片描述
可以看出来,热重载后的执行顺序是:reassemble->didUpdateWidget->build。当然reassemble只在debug模式下有效,实际使用中是没有的。接着我们点击App右上角跳转界面,控制台输出如下:
在这里插入图片描述
可以看到跳转界面后,我们的执行顺序是:deactivate->didChangeDependencies->build。返回其实和跳转界面一样的输出,这里就不看了,同样是上图的顺序。

生命周期重要方法解释

(1)initState

initState是State生命周期里第一个执行的方法,在实际项目中,当我们需要追加一些初始化方法的时候可以用其执行,比如animations,controllers。如果要重写这个方法,就需要在这个方法里加上super.initState()。在initState里,Framework层还未把Context和State关联到一起,因此还不能访问Context。另外,初始化方法在生命周期里只会执行一次。

(2)didChangeDependencies

该方法在执行完initState之后被执行,这个时候可以访问Context了。如果Widget使用了InheritedWidget的数据,并且在InheritedWidget的数据发生变化时,Flutter Framework层就会触发didChangeDependencies的回调,关于InheritedWidget知识这篇博文最后会详解,这里就不在赘述。

(3)build

build方法在执行完didChangeDependencies和didUpdateWidget之后被执行,每当调用setState(…)方法时都会执行它。

(4)dispose

dispose方法在组件被销毁时调用,一般执行initState初始化的动作时,dispose都有对应的销毁动作。

Widget的唯一身份标识:key

在Flutter里,每一个Widget都具有唯一标识,并且这个唯一标识时在Flutter Framework层创建和渲染时生成的,它就是key。如果key作为参数被传入Widget里面,则会根据指定的名字生成key。

在有些场景下,你需要保存key,并且通过key访问该Widget。这时,可以通过GlobalKey,LocalKey,UniqueKey或ObjectKey进行保存。例如,通过GlobalKey保存key并且在整个应用程序中共享,对应的代码 如下所示:

GlobalKey myKey=new GlobalKey();
@override
Widget build(BuildContext context){
	return new MyWidget(key:myKey);
}

InheritedWidget

InheritedWidget是一个比较特殊的组件,它被定义为父节点。被InheritedWidget暴露出来的数据,可以高效地在Widget树中从上往下传递共享,并支持跨级数据传递。用一张图表示InheritedWidget,如下图所示:
InheritedWidget比如在购物车应用中,每当我们添加商品到购物车的时候,InheritedWidget就会被重新创建,状态管理就介绍到这里,下一篇博文将介绍Flutter包管理。

发布了97 篇原创文章 · 获赞 129 · 访问量 83万+

猜你喜欢

转载自blog.csdn.net/liyuanjinglyj/article/details/104105816