一. StatefulWidget
1.1. 认识StatefulWidget
1.1.1. StatefulWidget介绍
定义到Widget中的数据都是不可变的,必须定义为final,为什么?
- Flutter在设计的时候就决定了一旦Widget中展示的数据发生变化,就重新构建整个Widget;
- Flutter通过一些机制来限定定义到Widget中的成员变量必须是final的;
Flutter如何做到我们在开发中定义到Widget中的数据一定是final的呢?
@immutableabstract class Widget extends DiagnosticableTree {// ...省略代码}
- 这实际上是一个 注解;
-
1.1.2. 如何存储Widget状态?
既然Widget是不可变,那么StatefulWidget如何来存储可变的状态呢?
StatelessWidget无所谓,其数据通常是直接定义完后就不修改的;
- StatefulWidget需要有状态(可以理解成变量)的改变;
Flutter将StatefulWidget设计成了两个类:
- 一个类继承自StatefulWidget,作为Widget树的一部分;
- 一个类继承自State,用于记录StatefulWidget会变化的状态,并且根据状态的变化,构建出新的Widget;
创建一个StatefulWidget:
- Flutter在构建Widget Tree时,会获取
State的实例,并且它调用build方法去获取StatefulWidget希望构建的Widget; 可以将需要保存的状态保存在MyState中,因为它是可变的;
class MyStatefulWidget extends StatefulWidget {@overrideState<StatefulWidget> createState() {// 将创建的State返回return MyState();}}class MyState extends State<MyStatefulWidget> {@overrideWidget build(BuildContext context) {return <构建自己的Widget>;}}
思考:为什么Flutter要这样设计呢?
Flutter中,只要数据改变了Widget就需要重新构建(rebuild)1.2. StatefulWidget案例
1.2.1. 案例效果和分析
计数器案例:
Column小部件:垂直方向布局;
- Row小部件:水平方向布局;
RaiseButton小部件:按钮,onPress属性是传入一个回调函数,当按钮点击时被回调;
1.2.2. 创建StatefulWidget
当点击按钮时,数字会发生变化,所以我们需要使用一个StatefulWidget,所以需要创建两个类;
- MyCounterWidget继承自StatefulWidget,里面需要实现createState方法;
MyCounterState继承自State,里面实现build方法,并且可以定义一些成员变量;
class MyCounterWidget extends StatefulWidget {@overrideState<StatefulWidget> createState() {// 将创建的State返回return MyCounterState();}}class MyCounterState extends State<MyCounterWidget> {int counter = 0;@overrideWidget build(BuildContext context) {return Center(child: Text("当前计数:$counter", style: TextStyle(fontSize: 30),),);}}
1.2.3. 实现按钮的布局
class MyCounterState extends State<MyCounterWidget> {int counter = 0;@overrideWidget 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;
如何让Flutter知道状态发生改变重新构建Widget呢?
- 需要调用一个State中默认提供的setState方法;
可以在其中的回调函数中修改我们的变量;
onPressed: () {setState(() {counter++;});},
1.3. StatefulWidget生命周期
1.3.1. 生命周期的理解
客户端开发:iOS开发中我们需要知道UIViewController从创建到销毁的整个过程,Android开发中我们需要知道Activity从创建到销毁的整个过程。以便在不同的生命周期方法中完成不同的操作;
Flutter小部件的生命周期:
- StatelessWidget可以由父Widget直接传入值,调用build方法来构建,整个过程非常简单;
- StatefulWidget需要通过State来管理其数据,并且还要监控状态的改变决定是否重新build整个Widget;
主要讨论StatefulWidget的生命周期,也就是它从创建到销毁的整个过程;
1.3.2. 生命周期的简单版
StatefulWidget有哪些生命周期的回调呢?分别在什么情况下执行?
灰色部分的内容是Flutter内部操作的,并不需要手动去设置它们;
- 白色部分表示可以去监听到或者可以手动调用的方法;
StatefulWidget本身由两个类组成的:StatefulWidget和State
执行StatefulWidget中相关的方法:
- 1、执行StatefulWidget的构造函数(Constructor)来创建出StatefulWidget;
- 2、执行StatefulWidget的createState方法,来创建一个维护StatefulWidget的State对象;
State object,调用createState创建State对象时,执行State类的相关方法:
- 1、执行State类的构造方法(Constructor)来创建State对象;
- mounted:Flutter内部操作
- 2、执行initState,我们通常会在这个方法中执行一些数据初始化的操作,或者也可能会发送网络请求;
- 注意:这个方法是重写父类的方法,必须调用super,因为父类中会进行一些其他操作;
- 注解(annotation):@mustCallSuper
- 3、执行didChangeDependencies方法,这个方法在两种情况下会调用
- 情况一:调用initState会调用;
- 情况二:从其他对象中依赖一些数据发生改变时,比如InheritedWidget;
- 4、Flutter执行build方法,来看一下我们当前的Widget需要渲染哪些Widget;
- 5、当前的Widget不再使用时,会调用dispose进行销毁;
- 6、手动调用setState方法,会根据最新的状态(数据)来重新调用build方法,构建对应的Widgets;
- 7、didUpdateWidget是在当父Widget触发重建(rebuild)时,系统会调用didUpdateWidget方法;
代码演示:
import 'package:flutter/material.dart';main(List<String> args) {runApp(MyApp());}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("HelloWorld"),),body: HomeBody(),),);}}class HomeBody extends StatelessWidget {@overrideWidget build(BuildContext context) {print("HomeBody build");return MyCounterWidget();}}class MyCounterWidget extends StatefulWidget {MyCounterWidget() {print("执行了MyCounterWidget的构造方法");}@overrideState<StatefulWidget> createState() {print("执行了MyCounterWidget的createState方法");// 将创建的State返回return MyCounterState();}}class MyCounterState extends State<MyCounterWidget> {int counter = 0;MyCounterState() {print("执行MyCounterState的构造方法");}@overridevoid initState() {super.initState();print("执行MyCounterState的init方法");}@override void didChangeDependencies() {// TODO: implement didChangeDependenciessuper.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),)],),);}@overridevoid didUpdateWidget(MyCounterWidget oldWidget) {super.didUpdateWidget(oldWidget);print("执行MyCounterState的didUpdateWidget方法");}@overridevoid dispose() {super.dispose();print("执行MyCounterState的dispose方法");}}
打印结果如下:
flutter: HomeBody buildflutter: 执行了MyCounterWidget的构造方法flutter: 执行了MyCounterWidget的createState方法flutter: 执行MyCounterState的构造方法flutter: 执行MyCounterState的init方法flutter: 执行MyCounterState的didChangeDependencies方法flutter: 执行执行MyCounterState的build方法// 注意:Flutter会build所有的组件两次flutter: HomeBody buildflutter: 执行了MyCounterWidget的构造方法flutter: 执行MyCounterState的didUpdateWidget方法flutter: 执行执行MyCounterState的build方法
改变状态,手动执行setState方法后会打印如下结果:
flutter: 执行执行MyCounterState的build方法
1.3.3. 生命周期的复杂版
1、mounted是State内部设置的一个属性;
- 内部设置的,不需要手动进行修改;
2、dirty state的含义是脏的State
- 它实际是通过一个Element的东西的属性来标记的;
- 将它标记为dirty会等待下一次的重绘检查,强制调用build方法来构建我们的Widget;
3、clean state的含义是干净的State
表示当前build出来的Widget,下一次重绘检查时不需要重新build;
二. Flutter的编程范式
2.1. 编程范式的理解
面向对象编程、面向过程编程、函数式编程、面向协议编程;
另外还有两个对应的编程范式:命令式编程 和 声明式编程命令式编程: 命令式编程非常好理解,就是一步步给计算机命令,告诉它想做什么事情;
声明式编程: 声明式编程通常是描述目标的性质,你应该是什么样的,依赖哪些状态,并且当依赖的状态发生改变时,我们通过某些方式通知目标作出响应;
2.3. Flutter的编程范式
目前在Vue、React、包括iOS中的SwiftUI中以及Flutter目前都采用了声明式编程。
声明式编程,通常会维护一套数据集:这个数据集可能来自己父类、来自自身State管理、来自InheritedWidget、来自统一的状态管理的地方;
- 有这么一个数据集,并且告诉Flutter这些数据集在哪里使用;
- 然后声明期望最后的状态是什么样的,Flutter内部帮助我们改变这个状态;
statelessWidget 转statulWidget alt + enter
将children抽取为一个独立的widget alt + enter + w
