Flutter(五)有状态的StatefulWidget

2021-06-12  本文已影响0人  AlanGe

一. StatefulWidget

在开发中,某些Widget情况下我们展示的数据并不是一层不变的:

比如Flutter默认程序中的计数器案例,点击了+号按钮后,显示的数字需要+1;

比如在开发中,我们会进行下拉刷新、上拉加载更多,这时数据也会发生变化;

而StatelessWidget通常用来展示哪些数据固定不变的,如果数据会发生改变,我们使用StatefulWidget;

1.1. 认识StatefulWidget

1.1.1. StatefulWidget介绍

如果你有阅读过默认我们创建Flutter的示例程序,那么你会发现它创建的是一个StatefulWidget。

为什么选择StatefulWidget呢?

但是有一个问题,我之前说过定义到Widget中的数据都是不可变的,必须定义为final,为什么呢?

Flutter如何做到我们在开发中定义到Widget中的数据一定是final的呢?

我们来看一下Widget的源码:

@immutable
abstract class Widget extends DiagnosticableTree {
    // ...省略代码
}

这里有一个很关键的东西@immutable

实际上官方有对@immutable进行说明:

图片

结论: 定义到Widget中的数据一定是不可变的,需要使用final来修饰

1.1.2. 如何存储Widget状态?

既然Widget是不可变,那么StatefulWidget如何来存储可变的状态呢?

Flutter将StatefulWidget设计成了两个类:

创建一个StatefulWidget,我们通常会按照如下格式来做:

class MyStatefulWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // 将创建的State返回
    return MyState();
  }
}

class MyState extends State<MyStatefulWidget> {
  @override
  Widget build(BuildContext context) {
    return <构建自己的Widget>;
  }
}

思考:为什么Flutter要这样设计呢?

这是因为在Flutter中,只要数据改变了Widget就需要重新构建(rebuild)

1.2. StatefulWidget案例

1.2.1. 案例效果和分析

我们通过一个案例来练习一下StatefulWidget,还是之前的计数器案例,但是我们按照自己的方式进行一些改进。

案例效果以及布局如下:

图片

1.2.2. 创建StatefulWidget

下面我们来看看代码实现:

class MyCounterWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // 将创建的State返回
    return MyCounterState();
  }
}

class MyCounterState extends State<MyCounterWidget> {
  int counter = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("当前计数:$counter", style: TextStyle(fontSize: 30),),
    );
  }
}
图片

1.2.3. 实现按钮的布局

class MyCounterState extends State<MyCounterWidget> {
  int counter = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              RaisedButton(
                color: Colors.redAccent,
                child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                onPressed: () {

                },
              ),
              RaisedButton(
                color: Colors.orangeAccent,
                child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                onPressed: () {

                },
              )
            ],
          ),
          Text("当前计数:$counter", style: TextStyle(fontSize: 30),)
        ],
      ),
    );
  }
}
图片

1.2.4. 按钮点击状态改变

我们现在要监听状态的改变,当状态改变时要修改counter变量

如何可以让Flutter知道我们的状态发生改变了,重新构建我们的Widget呢?

onPressed: () {
  setState(() {
    counter++;
  });
},

这样就可以实现想要的效果了:

图片

1.3. StatefulWidget生命周期

1.3.1. 生命周期的理解

什么是生命周期呢?

Flutter小部件的生命周期:

1.3.2. 生命周期的简单版

在这个版本中,我讲解那些常用的方法和回调,下一个版本中我解释一些比较复杂的方法和回调

那么StatefulWidget有哪些生命周期的回调呢?它们分别在什么情况下执行呢?

我们知道StatefulWidget本身由两个类组成的:StatefulWidgetState,我们分开进行分析

图片

首先,执行StatefulWidget中相关的方法:

其次,调用createState创建State对象时,执行State类的相关方法:

我们来通过代码进行演示:

import 'package:flutter/material.dart';

main(List<String> args) {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("HelloWorld"),
        ),
        body: HomeBody(),
      ),
    );
  }
}


class HomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("HomeBody build");
    return MyCounterWidget();
  }
}


class MyCounterWidget extends StatefulWidget {
  
  MyCounterWidget() {
    print("执行了MyCounterWidget的构造方法");
  }
  
  @override
  State<StatefulWidget> createState() {
    print("执行了MyCounterWidget的createState方法");
    // 将创建的State返回
    return MyCounterState();
  }
}

class MyCounterState extends State<MyCounterWidget> {
  int counter = 0;
  
  MyCounterState() {
    print("执行MyCounterState的构造方法");
  }

  @override
  void initState() {
    super.initState();
    print("执行MyCounterState的init方法");
  }
  
  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    print("执行MyCounterState的didChangeDependencies方法");
  }

  @override
  Widget build(BuildContext context) {
    print("执行执行MyCounterState的build方法");
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              RaisedButton(
                color: Colors.redAccent,
                child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                onPressed: () {
                  setState(() {
                    counter++;
                  });
                },
              ),
              RaisedButton(
                color: Colors.orangeAccent,
                child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                onPressed: () {
                  setState(() {
                    counter--;
                  });
                },
              )
            ],
          ),
          Text("当前计数:$counter", style: TextStyle(fontSize: 30),)
        ],
      ),
    );
  }

  @override
  void didUpdateWidget(MyCounterWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("执行MyCounterState的didUpdateWidget方法");
  }

  @override
  void dispose() {
    super.dispose();
    print("执行MyCounterState的dispose方法");
  }
}

打印结果如下:

flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行了MyCounterWidget的createState方法
flutter: 执行MyCounterState的构造方法
flutter: 执行MyCounterState的init方法
flutter: 执行MyCounterState的didChangeDependencies方法
flutter: 执行执行MyCounterState的build方法

// 注意:Flutter会build所有的组件两次(查了GitHub、Stack Overflow,目前没查到原因)
flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行MyCounterState的didUpdateWidget方法
flutter: 执行执行MyCounterState的build方法

当我们改变状态,手动执行setState方法后会打印如下结果:

flutter: 执行执行MyCounterState的build方法

1.3.3. 生命周期的复杂版(选读)

我们来学习几个前面生命周期图中提到的属性,但是没有详细讲解的

1、mounted是State内部设置的一个属性,事实上我们不了解它也可以,但是如果你想深入了解它,会对State的机制理解更加清晰;

2、dirty state的含义是脏的State

3、clean state的含义是干净的State

二. Flutter的编程范式

这个章节又讲解一些理论的东西,可能并不会直接讲授Flutter的知识,但是会对你以后写任何的代码,都具备一些简单的知道思想;

2.1. 编程范式的理解

编程范式对于初学编程的人来说是一个虚无缥缈的东西,但是却是我们日常开发中都在默认遵循的一些模式和方法

比如我们最为熟悉的 面向对象编程就是一种编程范式,与之对应或者结合开发的包括:面向过程编程、函数式编程、面向协议编程;

另外还有两个对应的编程范式:命令式编程声明式编程

上面的描述还是太笼统了,我们来看一些具体点的例子;

2.2. 前端的编程范式

下面的代码没有写过前端的可以简单看一下

下面的代码是在前端开发中我写的两个demo,作用都是点击按钮后修改h2标签的内容:

图片

2.3. Flutter的编程范式

从2009年开始(数据来自维基百科),声明式编程就开始流行起来,并且目前在Vue、React、包括iOS中的SwiftUI中以及Flutter目前都采用了声明式编程。

现在我们来开发一个需求:显示一个Hello World,之后又修改成了Hello Flutter

如果是传统的命令式编程,我们开发Flutter的模式很可能是这样的:(注意是想象中的伪代码)

final text = new Text();
var title = "Hello World";
text.setContent(title);

// 修改数据
title = "Hello Flutter";
text.setContent(title);

如果是声明式编程,我们通常会维护一套数据集:

var title = "Hello World";

Text(title); // 告诉Text内部显示的是title

// 数据改变
title = "Hello Flutter";
setState(() => null); // 通知重新build Widget即可

上面的代码过于简单,可能不能体现出Flutter声明式编程的优势所在,但是在以后的开发中,我们都是按照这种模式在进行开始,我们一起来慢慢体会;

参考:小码哥Flutter

上一篇 下一篇

猜你喜欢

热点阅读