Flutter状态管理学习手册(一)——ScopedModel

一、ScopedModel简介

ScopedModel属于入门级别的状态管理框架,它的思想比较简单,参考官方文档便可以很容易理解其中构架。

FlutterLifting state up(状态提升)是十分必要的,状态提升可以理解为把组件之间相互共享的状态提取出来放在一个较高层级中管理的一种思想。ScopedModel提供了对于这种状态管理的便利。

二、ScopedModel中的三个概念

ScopedModel主要有三个重要的概念,也是其中的三个类:ModelScopedModelScopedModelDescendantScopedModel基本上通过这三个类实现其功能。

Model是封装状态和状态操作的地方。我们可以将想要的数据存放在Model当中并且将对数据操作,如添加删除的相关方法放在这里。Model还提供了一个notifyListeners()方法,它的作用是当数据发生改变时,可以通过调用notifyListeners()方法通知界面进行更新。

ScopedModel是一个用于保存ModelWidget。通常ScopedModel会一个应用的入口处作为父布局使用,并以Model作为参数传入,使得ScopedModel持有Model

ScopedModel的子布局中,可以通过ScopedModel.of<Model>(context)方法来获取Model

ScopedModelDescendant,顾名思义,是ScopedModel的派生物。同样的,它也是一个WidgetScopedModelDescendant会作为ScopedModel下的子布局存在,它的主要作用是响应状态更新。

ScopedModelDescendant中存在builder函数,这个函数会在ModelnotifyListeners()发生时被调用,从而根据Model中的数据生成相应的界面。

三、ScopedModel的实践

这里以常见的获取列表选择列表为例子。一个页面用于展示选中项和跳转到列表,一个页面用于显示列表。

screencapture.gif

1. 引入scoped_model第三方库

在根目录的pubspec.yaml文件的dependencies中加入依赖

dependencies:
  ...
  scoped_model: ^1.0.0

2. 定义Model

创建一个ListModel类,这个类需要继承scoped_model包里的Model类。

ListModel类中包含三个状态:列表初始化标志、列表数据、选中的列表项。

  bool _init = false; // 列表初始化标志
  List<String> _list = []; // 列表数据
  String _selected = '未选中'; // 选中的列表项

Model中不仅只有数据,还包括对数据操作的方法,这里定义两个操作方法,分别是选中列表项目和加载列表的方法,并且,这两个方法在更新数据后,需要调用notifyListeners()通知UI更新。


  /**
   * 选中列表项
   */
  void select(String selected) {
    _selected = selected;

    // 通知数据变更
    notifyListeners();
  }

  /**
   * 加载列表
   */
  void loadList() async {
    // 模拟网络请求
    await Future.delayed(Duration(milliseconds: 3000));
    _list = [
      '1\. Scoped Model',
      '2\. Scoped Model',
      '3\. Scoped Model',
      '4\. Scoped Model',
      '5\. Scoped Model',
      '6\. Scoped Model',
      '7\. Scoped Model',
      '8\. Scoped Model',
      '9\. Scoped Model',
      '10\. Scoped Model'
    ];

    _init = true;
    // 通知数据变更
    notifyListeners();
  }

3. UI布局

在UI上,使用ScopedModel作为根布局,提供Model,使用ScopedModelDescendant作为子布局,响应Model

首先,在main()方法中,创建ListModel实例,用ScopedModel包裹MyApp布局

void main() {

  // 创建Model实例
  ListModel listModel = ListModel();
  // 使用ScopedModel作为根布局
  runApp(ScopedModel(model: listModel, child: MyApp()));
}

为了体现状态提升这一概念,例子中使用两个页面,一个是ShowPage,另一个是ListPageShowPage用于显示选中的列表项目和提供跳转到ListPage的入口,ListPage用于加载显示列表。

ShowPage中,显示ListModel中的选中项。

ScopedModelDescendant<ListModel>(
  builder: (context, child, model) {
    String selected = model.selected;
    return Text(selected);
  }
),

ScopedModelDescendant的泛型指明ListModel,它便会自动获取ScopedModel中的ListModel,在builder: (context, child, model)中即可通过其中的model参数获取状态,构建UI。

同样的,在ListPage中,通过ScopedModelDescendant来显示加载状态和列表。

body: ScopedModelDescendant<ListModel>(builder: (context, child, model) {
        // 根据状态显示界面
        if (!model.isInit) {
          // 显示loading界面
          return buildLoad();
        } else {
          // 显示列表界面
          var list = model.list;
          return buildList(list);
        }
      }),

4. 状态改变

ListPage是一个StatefulWidget,所以可以在initState()方法中进行列表的加载工作。

@override
void initState() {
  super.initState();
  ListModel model = ScopedModel.of<ListModel>(context); // 获取ListModel
  if (!model.isInit) {
    model.loadList(); // 加载列表
  }
}

点击列表中的某一项时,会选中该项,这时调用ListModel中的select(String)方法,并返回上一个界面。

onTap: () {
  ListModel model = ScopedModel.of<ListModel>(context);
  model.select(list[index]);
  // 返回到上一级页面
  Navigator.pop(context);
},

完整代码可以参考github.com/windinwork/…

四、ScopedModel的注意事项

  1. ScopedModelDescendant的层级需要尽量低,可以避免大范围的UI重建。这里引用官方的例子。
// 错误示例
return ScopedModelDescendant<CartModel>(
  builder: (context, child, cart) {
    return HumongousWidget(
      // ...
      child: AnotherMonstrousWidget(
        // ...
        child: Text('Total price: ${cart.totalPrice}'),
      ),
    );
  },
);
// 正确示例
return HumongousWidget(
  // ...
  child: AnotherMonstrousWidget(
    // ...
    child: ScopedModelDescendant<CartModel>(
      builder: (context, child, cart) {
        return Text('Total price: ${cart.totalPrice}');
      },
    ),
  ),
);

五、总结

ScopedModel功能比较简单,使用Model保存状态和通知状态改变,使用ScopedModel提供Model,使用ScopedModelDescendant布局来响应状态变化,是一个十分适合入门者理解的状态管理模型。

参考目录

Simple app state management

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

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,068评论 1 32
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,952评论 4 60
  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,787评论 1 22
  • 今天菡没放学,老师就发信息说今天语文测试一下,去接她回来路上就聊这了,问题出在哪了,她自己也说是粗心了。再考数学我...
  • 风吹打窗户的声音,汽车在马路奔驰而过的声音,还有脑袋中回响起的似声不是声的交织在一起。 与自我对话中,我发现我还是...
    炎雨冰阅读 254评论 0 0