写点什么

深入了解 Flutter 的状态管理机制(下)

作者:岛上码农
  • 2022 年 5 月 10 日
  • 本文字数:3064 字

    阅读完需:约 10 分钟

深入了解 Flutter 的状态管理机制(下)

前言

上篇介绍了使用自定义InheritedWidget子类 ModelBinding实现子组件直接访问状态,从而达到无需沿着组件树层层传递的目的,减少了父子组件间的耦合。但是也存在两个问题:


  • ModelBinding 类不通用,每个页面都需要自己定义一个 InheritedWidget 子类。

  • 状态改变的回调函数还是需要从顶层传递到具体操作状态的组件,耦合没有完全解除。


本篇将对上一个 ModelBinding 类进行改造,实现一个更通用,耦合度更低的 ModelBinding 类。

解耦状态更新回调

InheritedWidget 方式的版本将组件和 model 进行绑定后简化了模型的更新。现在,任何 ModelBinding 的下级组件都可以获取这个模型并更新它,因此也就无需通过回调来处理了。如前所述,通过 ViewModel.of(context)ModelBinding 的下级可以获取模型的值,从而使得依赖模型的组件可以自动跟随模型的变更进行更新。同样的,ModelBinding 的子组件也可以通过ViewModel.update(context, newModel)这个方式来更新模型数据。看起来不错哦!


为了支持使用静态的 ViewModel.update 方法来更新模型,我们需要引入额外一个额外的有状态组件。这会稍微有点复杂。


  • ModelBinding 变成了一个有状态组件,并且持有当前状态模型对象。

  • ModelBinding 构建了一个_ModelBindingScopeInheritedWidget 子组件,该子组件引用了 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 能够在其状态发生改变后,将依赖于该组件状态的全部下级组件进行更新。而通过泛型的使用,我们构建了一个最简单的通用状态管理组件。当然,实际应用中的状态管理远比这复杂,但是明白了其中的原理,对我们优化性能会有更大的帮助。



发布于: 刚刚阅读数: 3
用户头像

岛上码农

关注

用代码连接孤岛,公众号@岛上码农 2022.03.03 加入

从南漂到北,从北漂到南的业余码农

评论

发布
暂无评论
深入了解 Flutter 的状态管理机制(下)_flutter_岛上码农_InfoQ写作社区