Flutter | 状态管理探索篇——Scoped Model(一)

前言

Flutter的不少灵感来自于React,它的设计思想是数据与视图分离,由数据映射渲染视图。因此在Flutter中,它的Widget是immutable的,而它的动态部分所有放到了状态(State)中。react

假如你曾进行过react开发,也许你一下会想到Redux。flutter有相似redux的状态管理的库吗?答案是确定的,可是有关在flutter中使用redux的应用实践咱们会在以后的文章中进行介绍。git

这个系列将会从这几个状态管理方案进行深刻研究:github

  • Scoped_model
  • redux
  • BLoC
  • 对比总结篇

今天要和你们分享的是第一篇,使用Scoped_model进行状态管理。redux

为何须要状态管理

在咱们一开始构建应用的时候,也许很简单。咱们有一些状态,直接把他们映射成视图就能够了。这种简单应用可能并不须要状态管理。架构

可是随着功能的增长,你的应用程序将会有几十个甚至上百个状态。这个时候你的应用应该会是这样。app

Wow,这是什么鬼。咱们很难再清楚的测试维护咱们的状态,由于它看上去实在是太复杂了!并且还会有多个页面共享同一个状态,例如当你进入一个文章点赞,退出到外部缩略展现的时候,外部也须要显示点赞数,这时候就须要同步这两个状态。

这时候,咱们便迫切的须要一个架构来帮助咱们理清这些关系,状态管理框架应运而生。框架

什么是Scoped_model

Scoped_model是一个dart第三方库,提供了让您可以轻松地将数据模型从父Widget传递到它的后代的功能。此外,它还会在模型更新时从新渲染使用该模型的全部子项。less

它直接来自于Google正在开发的新系统Fuchsia核心Widgets 中对Model类的简单提取,做为独立使用的独立Flutter插件发布。ide

实现原理

Scoped model使用了观察者模式,将数据模型放在父代,后代经过找到父代的model进行数据渲染,最后数据改变时将数据传回,父代再通知全部用到了该model的子代去更新状态。post

而咱们则须要将它们放在顶层入口MaterialApp之上,这样就能进行全局的状态管理了。

这里page3,page4表明使用到该状态(model)的子页面。

Lets do it!

这里咱们以一个最简单的CountApp举例,详细介绍Scoped_model的用法。该项目完整代码已放在github仓库

这是一个在不一样页面使用Scoped共享状态信息的app。这两个页面都依赖于一个数字,这个数字会随着咱们按下按钮的次数而增长。

第一步:添加依赖

在pubspec中添加scoped_model的依赖。

第二步:建立Model

在Scoped中,Model是一个只包含与状态相关信息的单位。咱们应该把状态数据与操做数据的方法抽象出来封装到Model中。

import 'package:scoped_model/scoped_model.dart';

class CountModel extends Model{
  int _count = 0;
  get count => _count;
  
  void increment(){
    _count++;
    notifyListeners();
  }
}
复制代码
  • 咱们须要让咱们自定义的CountModel继承至Model。
  • 在状态发生变化时(increment)通知全部用到了该model的子项更新状态。(notifyListeners)

第三步:将Model放入顶层

//建立顶层状态
  CountModel countModel = CountModel();

  @override
  Widget build(BuildContext context) {
    return ScopedModel<CountModel>(
      model: countModel,
      child: new MaterialApp(
        home: TopScreen(),
      ),
    );
  }
复制代码
  • 咱们在顶层建立了一个CountModel的实例。
  • ScopedModel<T extends Model>是一个StatelessWidget,它接收一个model,并提供给须要它的全部部件。
  • 将ScopedModel<T extends Model>的model属性绑定咱们的CountModel对象。

第四步:在子页面中获取Model

咱们能够从前面的演示图片中看出,一共有两个页面,都使用了同一个model。 Scoped_model提供了两种方式在子页面中获取model。咱们先来介绍第一种,使用ScopedModelDescendant获取model。

@override
  Widget build(BuildContext context) {
    return ScopedModelDescendant<CountModel>(
      builder: (context,child,model){
        return Scaffold(
          body: Center(
            child: Text(
              model.count.toString(),
              style: TextStyle(fontSize: 48.0),
            ),
          ),
        );
      },
    );
  }
复制代码
  • ScopedModelDescendant<T extends Model>是一个Stateless Widget,它接收三个参数。
  • builder是一个ScopedModelDescendantBuilder,它接收三个参数。
    ,在builder中可以经过model来获取CountModel实例。
  • rebuildOnChange属性可以控制当该状态发生变化时,是否rebuild,做用等同于setState。也就是说咱们调用改变状态的一些方法时,没必要再setState。
floatingActionButton: new FloatingActionButton(
          onPressed: () => model.increment(),
          tooltip: 'Increment',
          child: new Icon(Icons.add),
        )
复制代码

第二种获取model的方式——使用ScopedModel.of

final countModel = ScopedModel.of<CountModel>(context);
countModel.increment();
复制代码

或者在Model中重写of方法

class CountModel extends Model{
  int _count = 0;
  get count => _count;

  void increment(){
    _count++;
    notifyListeners();
  }
//重写of方法
  CountModel of(context) =>
      ScopedModel.of<CountModel>(context);
}
复制代码

而后直接经过CountModel获取model实例

final countModel2 = CountModel().of(context);
复制代码

这种方式彷佛让咱们的代码有更好的可阅读性。

【注意:】咱们在使用第二种方式的时候,rebuildOnChange属性默认为false,因此会致使没法刷新(同步)状态的状况发生,须要手动指定rebuildOnChange:true。这里要很是感谢@荣毅coolboy同窗的分享!

Q&A

这里看上去彷佛只添加了一个model,我应该如何添加多个model

要解决这个问题很简单,使用Mixin!

class MainModel extends Model with AModel,BModel,CModel{}
复制代码

而后将MainModel放在顶层便可。 这里有一个比较完整的使用ScopedModel管理状态的应用,详细用法可参考该项目。

Scoped是如何作到同步不一样页面中的状态的

Model实现了Listenable接口,并重写了void addListener(VoidCallback listener),removeListener(VoidCallback listener)方法,实现了观察者模式。 每当咱们调用notifyListeners()方法时,将会通知观察者更新状态。

Scoped如何作到数据可以互相共享的

在不一样页面间的数据传递使用了InheritedWidget。

侵入性

因为Model必须继承至Model类,因此它就具备了侵入性。之后假如不用scoped进行状态管理那么必然会带来须要更改多处代码的状况。这并非咱们但愿看到的结果。

写在最后

在flutter中,Scoped_model是一种很是简单易上手,并能保持代码高可阅读性的一种新的状态管理方式,值得各位去尝试一下!

本次所用到的代码已经上传Github: github.com/Vadaski/Vad…

若是您对scoped还有任何疑问或者文章的建议,欢迎在下方评论区以及个人邮箱1652219550a@gmail.com与我联系,我会及时回复!

下一章咱们将探索Redux在Flutter中的实践,敬请关注。

相关文章
相关标签/搜索