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

1、ScopedModel简介

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

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

2、ScopedModel中的三个概念

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

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

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

ScopedModel的子布局中,能够经过ScopedModel.of<Model>(context)方法来获取Model框架

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

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

3、ScopedModel的实践

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

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/…

4、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}');
      },
    ),
  ),
);
复制代码

5、总结

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

参考目录

Simple app state management

相关文章
相关标签/搜索