Flutter(或者更宽泛的app开发)可以一句话总结为:
UI = f(state)
即app的当前界面(UI)反映了当前程序状态(state). Flutter的状态管理就是app根据当前app的state的显示和更新UI.
我们假设state是一个类型为T的object, 在下文的购物车例子中, state是当前购物车的内容(类型T=List<Item>).
关于如何在widget tree里获取state, 可以有provider/riverpod等选项, e.g.Provider<T>. 但是如果直接用T作为状态会有问题: 就是当state的值被修改以后, 对应的UI并不会自动rebuild(除非其他逻辑如setState触发了rebuild).
如果希望在state修改以后, UI自动触发监听它的UI更新, 则需要用各种Notifier来包裹state. 例如我们不用T作为state, 而是用ValueNotifier<T>.
这里主要介绍和比较三种常用的Notifier: ChangeNotifier, ValueNotifier 和 StateNotifier.
本文参考了Resocoder, Andrea Bizzotto以及FunWithFlutter的tutorial.
ChangeNotifier
ChangeNotifier是Flutter SDK自带的class: 一个ChangeNotifier可以被widgets监听(listen), state改变后, 如果要让所有监听它的widget重新build, 需要手动调用notifyListeners()函数.
cf. Flutter官方的state management文档.
比如文档里的例子, 我们的state是当前购物车的内容, 于是我们定义CartModel类(继承ChangeNotifier)作为state:
class CartModel extends ChangeNotifier {
final List<Item> _items = []; // 购物车内容, 设为private
// 只读的购物车内容(getter)
UnmodifiableListView<Item> get items => UnmodifiableListView(_items);
int get totalPrice => _items.length * 42; // 当前购物车总价的getter(假设每件都是42块)
/// 加入物品到购物车
void add(Item item) {
_items.add(item);
notifyListeners(); // <==***This call tells the widgets that are listening to this model to rebuild.
}
/// 清空购物车
void removeAll() {
_items.clear();
notifyListeners(); // <==***This call tells the widgets that are listening to this model to rebuild.
}
}
ChangeNotifier结合Provider(ChangeNotifierProvider), 就是一个比较基础的state management解决方案.
ValueNotifier
ChangeNotifier有个问题是每次都要手动调用notifyListeners()才能让监听的widget更新.
如果想每当数据发生变化的时候, 都自动通知监听的widget -- 这就可以用ValueNotifier<T>.
ValueNotifier是ChangeNotifier的子类. 它把数据保存到T value里, 每当value发生变化的时候, 监听的widget都重新build, 省去了notifyListeners()这一步.
还是上面的例子, 改为用ValueNotifier就是:
-class CartModel extends ChangeNotifier {
+class CartModel extends ValueNotifier<List<Item>> {
- final List<Item> _items = []; // 不用声明_items以及items getter了 -- ValueNotifier自动把数据保存在`value`
+ CartModel(): super(<Item>[]); // 构造函数要提供value的初始值
- int get totalPrice => _items.length * 42;
+ int get totalPrice => value.length * 42;
void add(Item item) {
+ value.add(item);
- _items.add(item);
- notifyListeners(); // 不必call notifyListeners()
}
void removeAll() {
+ value.clear(item);
- _items.clear();
- notifyListeners();
}
}
对于provider, ValueNotifier也有对应的ValueListenableProvider供使用.
如果不用provider/riverpod, Flutter SDK里也有ValueListenableBuilder可以在value更改时自动rebuild -- 类似provider的Consumer.
StateNotifier
StateNotifier不是Flutter SDK自带, 需要使用state_notifier或者flutter_riverpod包. 也有对应的StateNotifierProvider(来自flutter_state_notifier包), 用法和ValueListenableProvider类似.
Flutter的ValueNotifier主要有以下问题:
ValueNotifier属于Flutter而不是纯dart, 不能用于写非flutter的库;value是public成员 -- 也就是说任何人都可以在外部修改ValueNotifier.value, 听上去很不安全
StateNotifier就解决了这两个问题:
- 它不依赖flutter, 可用于纯dart的开发;
- 在
StateNotifier类之外修改state的时候, 会有linting提示.
StateNotifier用法和ValueNotifier几乎一样, 只不过value变成了state, 迁移起来应该很容易:
-class CartModel extends ValueNotifier<List<Item>> {
+class CartModel extends StateNotifier<List<Item>> {
CartModel(): super(<Item>[]); // 构造函数要提供state的初始值
- int get totalPrice => value.length * 42;
+ int get totalPrice => state.length * 42;
void add(Item item) {
+ state.add(item);
- value.add(item);
}
void removeAll() {
+ state.clear();
- value.clear();
}
}
总结
TLDR: StateNotifier是坠好的.
ChangeNotifier |
ValueNotifier<T> |
StateNotifier<T> |
|
|---|---|---|---|
| Flutter SDK | Flutter SDK | state_notifier package | |
| 获取state | 手写getter | .value |
.state |
| 修改state | 手写setter或者其他function | value是public, 任何人都可修改 |
在外部修改state会有lint提示 |
| 状态变化时触发监听UI更新 | 手动notifyListeners() |
自动 | 自动 |
另外StateNotifier推荐结合freezed package使用, 以后有时间再总结一下freezed的用法以及它好在哪里.
Disqus 留言