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

前言

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

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

今天要和大家分享的是使用Scoped_model进行状态管理。

什么是Scoped_model

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

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

实现原理

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

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

image

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

Lets do it!

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

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


image

第一步:添加依赖

在pubspec中添加scoped_model的依赖。


image

第二步:创建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

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,它接收三个参数。


    image
  • builder是一个ScopedModelDescendantBuilder<T>,它接收三个参数。


    image

    ,在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);

这种方式似乎让我们的代码有更好的可阅读性。

Q&A

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

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

class MainModel extends Model with AModel,BModel,CModel{}

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

Scoped是如何做到同步不同页面中的状态的

image

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

Scoped如何做到数据能够互相共享的

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

image

是否具有侵入性

正在研究当中...

写在最后

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

本次所用到的代码已经上传Github: https://github.com/Vadaski/Vadaski-flutter_note_book/tree/master/mecury_project/example/scoped_demo

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容

  • 作为系列文章的第二篇,本篇将为你着重展示:如何搭建一个通用的Flutter App 常用功能脚手架,快速开发一个完...
    恋猫月亮阅读 33,946评论 13 99
  • 年前最后一天上班,今天很放纵,消费了不少。一杯大杯本周,一部场面宏大刺激的imax电影星球大战外传,一顿定食,一杯...
    Hirune阅读 154评论 0 0
  • 遗然世外的古庙,只因曲径通幽里,穿越岁月而来的美好祝福,多了偌多的朝拜者,无论来时初心如何,到了,必然是各种形式的...
    陌上花开l阅读 443评论 0 2
  • 自定义错误页面 开发环境,当 APP_DEBUG = true 时,使用默认错误页面; 生产环境,当 APP_DE...
    分析阅读 1,674评论 0 1
  • 濛濛细雨随夜来 千枝万树绿红白 五颜六色筝飞天 莺歌燕舞童颜开
    山东聊城李超阅读 188评论 0 1