原文链接https://siques.cn/p/41
这个章节我们会先介绍一下 Flutter 的 StatelessWidget … 还有 StatefulWidget …
然后会用一个非常简单的例子来理解一下 Flutter 应用的状态管理 … 状态就是小部件里的数据 … 小部件可以自己管理需要的数据 … 这些数据也可以通过小部件的构造函数从它的父辈那里传递过来 …
我们还会学一下,使用 InheritedWidget … 还有 ScopedModel,更有效的去把数据传递给需要的小部件 …
小部件
StatelessWidget:无变化状态的小部件
StatelessWidget,这种小部件里面不包含可以变化的状态,State 指的就是状态,状态可以想成是小部件里的数据 … 也就是 StatelessWidget 小部件一但被创建以后 … 它里面的状态一般不会有什么变化 …
下面再把 count 的值输出到控制台上检查一下 … 打开调试控制台 …
按一下界面上的这个漂浮按钮 … 你会发现,每次按这个按钮的时候,count 的值都会加上 1 … 不过在界面上这个 Chip 里面显示的 count 的值没有发生改变 …
我们创建的小部件的状态会根据用户行为发生变化 … 这个时候我们需要考虑使用另一种小部件,StatefulWidget …
StatefulWidget:带变化状态的小部件
StatefulWidget,这种类型的小部件里面可以有一些能够变化的状态 … 也就是 StaefulWidget 构建的用户界面可以动态的发生变化 … 比如在我们这个例子里面 … 点了这个漂浮按钮,界面上显示的这个数字要动态的发生变化 … 这种情况我们就可以使用一个 StaefulWidget …
下面可以把我们这个小部件改造成一个 StatefulWidget … 让它继承一下 StatefulWidget … 要注意的是 StaefulWidget 小部件本身也是 immutable ,不可改变的 …
小部件需要的可以变化的那些状态要单独放在一个 State 对象里面 … 这个 State 对象可以使用小部件里的 createState 这个方法来创建 .
然后到模拟器上再试一下 … 按一下这个动作按钮 … count 的值会加上 1,这个变化是在 setState 方法里面完成的,所以每次有变化,小部件都会使用新的状态被重建 … 重建以后,界面上会显示小部件变化之后的状态 …
状态管理(由父辈管理状态)
在我们这个 StateManagementDemo 小部件里面 … 小部件的状态是它自己管理的 … 它需要的所有的状态都在这个小部件自己这里 … 有时候这个状态可能在小部件的父辈那里 … 把 State 从父辈那里传递过来,可以通过小部件的构造函数 …
使用这个 Counter 的时候,现在需要提供一个 count 属性 … 这个属性的值可以使用上面定义的这个 _count …
现在 Counter 小部件里面需要的数据是从它爸爸,也就是 StateManagementDemo 那里传递过来的 …
从父辈那里传递个回调
下面我们再改造一下这个 Counter 小部件 … 把这个 Chip 换成一个 ActionChip ,这样可以给它一个 onPressed 属性,设置一下点按这个 Chip 要执行的动作 … 我想在按它的时候也可以让 _count 的值加上 1 …
如果在这个 Counter 小部件里面设置它的这个 count ,界面上显示的数字是不会有什么变化的 … 因为这个数字其实是 StateManagementDemo 里面的 _count 的的值 …
按一下界面上的这个 ActionChip … 执行的就是它爸爸传递过来的一个回调 … 做的事儿就是让小部件的 _count 的值加上 1 …
再按一下漂浮动作按钮 … 同样可以增加 _count 的值 …
状态树
现在,在 Counter 里面需要的数据是通过小部件的构造函数,从 StateManagementDemo 那里直接传递过来的 … 假设在这两个小部件之间还有一个小部件 … 比如可能有一个 CounterWrapper … 在这个 CounterWrapper 里面使用了 Counter …
这样,这个 Counter 需要的数据要先从 StateManagementDemo 传递给 CounterWrapper … 然后再由这个 CounterWrapper 传递给 Counter … 需要一级一级往下传 …
InheritedWidget
InheritedWidget:直接把数据传递给需要的小部件
在我们这个示例里面,Counter 小部件需要的数据是从 StateManagementDemo 传递给 CounterWrapper,又从 CounterWrapper 传递给了 Counter … 现在我们需要一种方法,可以把数据直接传递给需要的小部件 … 也就是 Counter 需要的数据可以不通过 CounterWrapper,而是直接从 StateManagementDemo 那里传递过来 …
可以试一下 Flutter 的 InheritedWidget … 用法就是可以去创建一个 InheritedWidget,在这个小部件里面设置其它小部件需要的数据,然后把这个 InheritedWidget 放在小部件树的某个地方,这样在树下面的小部件都可以直接访问到在 InheritedWidget 小部件里的数据了 …
ScopedModel
ScopedModel:安装与基本用法介绍
ScopedModel 也可以把数据直接交给需要的小部件 … 它是一个第三方的包,所以要使用它得先去安装一下 … 打开项目下面的 pubspec.yaml …
使用 ScopedModel … 我们得先去创建一个 Model … 在里面添加需要的数据 … 然后把 ScopedModel 小部件放到 Widget Tree 的某个位置上 … 设置一下它的 model … 这样在它下面的小部件都可以直接访问到它设置的 model 里的东西 …
使用 ScopedModel 传递数据
先创建一个 Model … 添加一个类 … 名字可以是 CounterModel … 它要继承一下 Model … 再导入需要的包 … 就是之前我们安装的这个 scoped_model …
里添加一个 int _count … 让它先等于 0 … 再添加一个 getter 方法, 名字叫 count … 让它返回 _count 的值 … 在使用了这个 model 的小部件里面,可以使用这个 getter 方法获取到 _count 的值 …
按一下界面上的 ActionChip ,会让 CounterModel 的 _count 的值增加 1 ,有变化就会重建这个部件显示出变化之后的样子 …
同样,按一下漂浮动作按钮,也可以让 CounterModel 里的 _count 的值加上 1 …
最终代码
import 'package:flutter/material.dart';
import "package:scoped_model/scoped_model.dart";
class StateManagementDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScopedModel(
model: CounterModel(),
child: Scaffold(
appBar: AppBar(
title: Text('StateManagementDemo'),
elevation: 0.0,
),
body: CouterWrapper(),
floatingActionButton: ScopedModelDescendant<CounterModel>(
rebuildOnChange: false,
builder: (context, _, model) => FloatingActionButton(
child: Icon(Icons.add),
onPressed: model.increaseCount,
),
)));
}
}
class CouterWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Counter(),
);
}
}
class Counter extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScopedModelDescendant<CounterModel>(
builder: (context, _, model) => ActionChip(
label: Text('${model.count}'),
onPressed: model.increaseCount,
));
}
}
class CounterProvider extends InheritedWidget {
final int count;
final VoidCallback increaseCount;
final Widget child;
// 构造函数
CounterProvider({this.count, this.increaseCount, this.child})
: super(child: child);
static CounterProvider of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType();
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
class CounterModel extends Model {
int _count = 0;
int get count => _count;
void increaseCount() {
_count += 1;
notifyListeners();
}
}