这里我们以一个最简单的CountApp举例,详细介绍Scoped_model的用法。该项目完整代码已放在github仓库。
这是一个在不同页面使用Scoped共享状态信息的app。这两个页面都依赖于一个数字,这个数字会随着我们按下按钮的次数而增加。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ujk83uiI-1630663346547)(https://user-gold-cdn.xitu.io/2018/9/11/165c864da39b5296?imageslim)]
第一步:添加依赖
在pubspec中添加scoped_model的依赖。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fFNMPAV4-1630663346549)(https://user-gold-cdn.xitu.io/2018/9/17/165e7f1aa9eb35e3?imageView2/0/w/1280/h/960/ignore-error/1)]
- 实际添加请参考:pub.dartlang.org/packages/sc…
- 由于版本冲突添加失败请参考:juejin.im/post/684490…
第二步:创建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是一个StatelessWidget,它接收一个model,并提供给需要它的所有部件。
- 将ScopedModel的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是一个Stateless Widget,它接收三个参数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GBqwxlLA-1630663346550)(https://user-gold-cdn.xitu.io/2018/9/17/165e8034b2584374?imageView2/0/w/1280/h/960/ignore-error/1)]
-
builder是一个ScopedModelDescendantBuilder,它接收三个参数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-373JEsJZ-1630663346552)(https://user-gold-cdn.xitu.io/2018/9/17/165e80f4c271a7b1?imageView2/0/w/1280/h/960/ignore-error/1)]
,在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是如何做到同步不同页面中的状态的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZjIakVHX-1630663346554)(https://user-gold-cdn.xitu.io/2018/9/17/165e82557e8565e8?imageView2/0/w/1280/h/960/ignore-error/1)]
Model实现了Listenable接口,并重写了void addListener(VoidCallback listener),removeListener(VoidCallback listener)方法,实现了观察者模式。 每当我们调用notifyListeners()方法时,将会通知观察者更新状态。
Scoped如何做到数据能够互相共享的
在不同页面间的数据传递使用了InheritedWidget。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQWIDbuz-1630663346555)(https://user-gold-cdn.xitu.io/2018/9/18/165e856263e9d6b2?imageView2/0/w/1280/h/960/ignore-error/1)]
模式。 每当我们调用notifyListeners()方法时,将会通知观察者更新状态。
Scoped如何做到数据能够互相共享的
在不同页面间的数据传递使用了InheritedWidget。
[外链图片转存中…(img-WQWIDbuz-1630663346555)]