前言
上篇介绍了使用自定义InheritedWidget
子类 ModelBinding
实现子组件直接访问状态,从而达到无需沿着组件树层层传递的目的,减少了父子组件间的耦合。但是也存在两个问题:
本篇将对上一个 ModelBinding
类进行改造,实现一个更通用,耦合度更低的 ModelBinding
类。
解耦状态更新回调
InheritedWidget
方式的版本将组件和 model
进行绑定后简化了模型的更新。现在,任何 ModelBinding
的下级组件都可以获取这个模型并更新它,因此也就无需通过回调来处理了。如前所述,通过 ViewModel.of(context)
,ModelBinding
的下级可以获取模型的值,从而使得依赖模型的组件可以自动跟随模型的变更进行更新。同样的,ModelBinding
的子组件也可以通过ViewModel.update(context, newModel)
这个方式来更新模型数据。看起来不错哦!
为了支持使用静态的 ViewModel.update
方法来更新模型,我们需要引入额外一个额外的有状态组件。这会稍微有点复杂。
将 ModelBinding
变成了一个有状态组件,并且持有当前状态模型对象。
为ModelBinding
构建了一个_ModelBindingScope
的 InheritedWidget
子组件,该子组件引用了 State<ModelBinding>
——即_ModelBingingState
,其实就相当于是引用了父级的状态。
为了改变 ModelBinding
当前模型的值,从而在调用 setState
方法重建 ModelBinding
,并重建下级的_ModelBindingScope
。
通过_ModelBindingScope
来实现ViewModel
类的静态获取模型对象及更新对象
代码如下所示:
static ViewModel of(BuildContext context) {
_ModelBindingScope scope =
context.dependOnInheritedWidgetOfExactType(aspect: _ModelBindingScope);
return scope.modelBindingState.currentModel;
}
static void update(BuildContext context, ViewModel newModel) {
_ModelBindingScope scope =
context.dependOnInheritedWidgetOfExactType(aspect: _ModelBindingScope);
scope.modelBindingState.updateModel(newModel);
}
复制代码
现在,任何 ModelBinding
的子组件都可以使用这些方法来更新模型数据了,下面的按钮代码就同时获取和更新了模型的数据。
Widget build(BuildContext context) {
return ElevatedButton(
child: Text('Hello World ${ViewModel.of(context).value}'),
onPressed: () {
ViewModel model = ViewModel.of(context);
ViewModel.update(context, ViewModel(value: model.value + 1));
},
);
}
复制代码
运行一下,一切正常(完整代码:model_binding_v1.dart),是不是该庆祝一下?然而,如果我们有技术上百个状态的话我们要写几十上百个 ModelBinding
类,而且每个 Model 都要提供一个静态的 of(context)
和 update
方法?
使用泛型构建通用的 ModelBinding 类
我们要保证 ModelBinding 的通用性,那就需要使用泛型来动态绑定状态模型对象。首先从最底层改起,先修改_ModelBIndingScope 类:
class _ModelBindingScope<T> extends InheritedWidget {
_ModelBindingScope({
Key key,
@required this.modelBindingState,
Widget child,
}) : assert(modelBindingState != null),
super(key: key, child: child);
final _ModelBindingV2State<T> modelBindingState;
@override
bool updateShouldNotify(_ModelBindingScope oldWidget) => true;
}
复制代码
改起来很简单,只需要加上泛型参数就好了。接下来是_ModelBindV2State,这个类也许改成泛型。
class _ModelBindingV2State<T> extends State<ModelBindingV2<T>> {
T currentModel;
@override
void initState() {
super.initState();
currentModel = widget.create();
}
void updateModel(T newModel) {
if (currentModel != newModel) {
setState(() {
currentModel = newModel;
});
}
}
@override
Widget build(BuildContext context) {
return _ModelBindingScope<T>(
modelBindingState: this,
child: widget.child,
);
}
}
复制代码
实际上,也只是增加了泛型参数,这里有个地方需要注意的是,之前我们是在_ModelBidingV2State 中直接构建初始状态了,现在由于是泛型,我们没法直接构建泛型对象,因此需要从有状态组件中获取。这里我们在 initState 中调用了 ModeBinding 类的 create 方法返回一个泛型对象(当然,也可以直接使用赋值,取决于 ModelBinding 类如何获取初始状态对象)。
接下来是对 ModelBinding 类进行改造,这里一个是需要传递一个获取泛型对象的方法 create 给构造函数,另外就是将之前放在 ViewModel 的获取模型对象的方法和更新方法提升到 ModelBinding 类来,并且变成泛型方法,从而对外只需要 ModelBinding 类就可以完成状态模型对象的获取和更新,最大程度简化状态模型对象的实现。
class ModelBindingV2<T> extends StatefulWidget {
ModelBindingV2({Key key, @required this.create, this.child})
: assert(create != null),
super(key: key);
final ValueGetter<T> create;
final Widget child;
@override
_ModelBindingV2State<T> createState() => _ModelBindingV2State<T>();
static T of<T>(BuildContext context) {
_ModelBindingScope<T> scope =
context.dependOnInheritedWidgetOfExactType(aspect: _ModelBindingScope);
return scope.modelBindingState.currentModel;
}
static void update<T>(BuildContext context, T newModel) {
_ModelBindingScope<T> scope =
context.dependOnInheritedWidgetOfExactType(aspect: _ModelBindingScope);
scope.modelBindingState.updateModel(newModel);
}
}
复制代码
改造完成之后,我们的实际 Controller 的代码就变成下面这样了:
class StateViewControllerV2 extends StatelessWidget {
StateViewControllerV2({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('模型绑定泛型版'),
),
body: Center(
child: ModelBindingV2(
create: () => ViewModel(),
child: ViewController(),
),
),
);
}
}
class ViewController extends StatelessWidget {
const ViewController({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
child: Text('Hello World ${ModelBindingV2.of<ViewModel>(context).value}'),
onPressed: () {
ViewModel model = ModelBindingV2.of<ViewModel>(context);
ModelBindingV2.update(context, ViewModel(value: model.value + 1));
},
);
}
}
复制代码
可以看到,整个 ModelBinding 类完成了状态的获取和更新,而且适用于任何状态模型类。
总结
本篇代码已经上传至:状态管理示例。从上一篇和本篇来看,状态管理的核心组件其实是 InheritedWidget。借助 InheritedWidget 能够在其状态发生改变后,将依赖于该组件状态的全部下级组件进行更新。而通过泛型的使用,我们构建了一个最简单的通用状态管理组件。当然,实际应用中的状态管理远比这复杂,但是明白了其中的原理,对我们优化性能会有更大的帮助。
评论