Flutter状态管理之ScopedModel

简介

ScopedModel 允许将数据模型从父Widget传递到后代,会在模型更新时重新渲染使用该模型的所有子项。

安装依赖

pubspec.yml中添加scoped_model的依赖。

scoped_model: ^1.0.1

创建模型

1
class CounterModel extends Model {
2
  int _count = 0;
3
  int get count => _count;
4
  void increment() {
5
    _count++;
6
    notifyListeners();
7
  }
8
  void reduce() {
9
    _count--;
10
    notifyListeners();
11
  }
12
  static CounterModel of(BuildContext context) => ScopedModel.of<CounterModel>(context);
13
}
  • 让我们自定义的CounterModel继承Model.
  • 当调用increment方法时改变了数据,然后notifyListeners会通知所有用到该模型的子项更新状态

将MaterialApp用ScopedModel包装起来

1
class MyApp extends StatelessWidget {
2
  @override
3
  Widget build(BuildContext context) {
4
    return ScopedModel<CounterModel>(
5
      model: CounterModel(),
6
      child: MaterialApp(
7
        theme: ThemeData(
8
          primarySwatch: Colors.yellow,
9
        ),
10
        debugShowCheckedModeBanner: false,
11
         home: ScopedModelDemo(),
12
      ),
13
    );
14
  }
15
}
  • ScopedModel 接受两个参数:model 和 child .
  • model 我们传入 CounterModel 实例

在子页面获取Model

1
class ScopedModelDemo extends StatelessWidget {
2
  @override
3
  Widget build(BuildContext context) {
4
    return Scaffold(
5
      appBar: AppBar(
6
        title: Text('ScopedModelDemo'),
7
        elevation: 0.0,
8
      ),
9
      body: ScopedModelDemoHome(),
10
      floatingActionButton: FloatingActionButton(
11
        onPressed: () {
12
          Navigator.of(context)
13
              .push(MaterialPageRoute(builder: (context) => SecondPage()));
14
        },
15
        tooltip: 'Increment',
16
        child: Icon(Icons.open_in_new),
17
      ),
18
    );
19
  }
20
}
21
22
class ScopedModelDemoHome extends StatefulWidget {
23
  @override
24
  _ScopedModelDemoHomeState createState() => _ScopedModelDemoHomeState();
25
}
26
27
class _ScopedModelDemoHomeState extends State<ScopedModelDemoHome> {
28
  @override
29
  Widget build(BuildContext context) {
30
    return Center(
31
      child: Column(
32
        mainAxisAlignment: MainAxisAlignment.center,
33
        children: <Widget>[
34
          ScopedModelDescendant<CounterModel>(
35
            rebuildOnChange: false,
36
            builder: (context, child, model) {
37
              print('add');
38
              return FlatButton(
39
                onPressed: model.increment,
40
                child: Icon(Icons.add),
41
                color: Colors.blue,
42
              );
43
            },
44
          ),
45
          ScopedModelDescendant<CounterModel>(
46
            builder: (context, child, model) {
47
              print('Text ---${model.count}');
48
              return Text(
49
                model.count.toString(),
50
              );
51
            },
52
          ),
53
          ScopedModelDescendant<CounterModel>(
54
            rebuildOnChange: false,
55
            builder: (context, child, model) {
56
              print('remove');
57
              return FlatButton(
58
                onPressed: model.reduce,
59
                child: Icon(Icons.remove),
60
                color: Colors.blue,
61
              );
62
            },
63
          ),
64
        ],
65
      ),
66
    );
67
  }
68
}
69
70
class SecondPage extends StatefulWidget {
71
  @override
72
  _SecondPageState createState() => _SecondPageState();
73
}
74
75
class _SecondPageState extends State<SecondPage> {
76
  @override
77
  Widget build(BuildContext context) {
78
    final count =
79
        ScopedModel.of<CounterModel>(context, rebuildOnChange: true).count;
80
    final counterModel =
81
        ScopedModel.of<CounterModel>(context, rebuildOnChange: false);
82
83
    return Scaffold(
84
      appBar: AppBar(
85
        title: Text('SecondPage'),
86
        elevation: 0.0,
87
      ),
88
      body: Center(
89
        child: Column(
90
          mainAxisAlignment: MainAxisAlignment.center,
91
          children: <Widget>[
92
            Text('$count'),
93
            FlatButton(
94
              onPressed: counterModel.reduce,
95
              child: Icon(Icons.remove),
96
              color: Colors.blue,
97
            )
98
          ],
99
        ),
100
      ),
101
      floatingActionButton: FloatingActionButton(
102
        onPressed: (){
103
          counterModel.increment();
104
        },
105
        tooltip: 'Increment',
106
        child: new Icon(Icons.add),
107
      ),
108
    );
109
  }
110
}

Scoped_model 提供了两种获取model的方式。

第一种:ScopedModelDescendant.
rebuildOnChange用来控制当该状态发生变化时,是否rebuild。

ScopedModelDescendant<CounterModel>(
   rebuildOnChange: true,
   builder: (context, child, model) {
     print('Text ---${model.count}');
     return Text(
        model.count.toString(),
     );
  },
),

第二章:ScopedModel.of
需要在Model中重写of方法。

static CounterModel of(BuildContext context) => ScopedModel.of(context);

然后直接通过CounterModel获取model实例

final counterModel = ScopedModel.of(context, rebuildOnChange: false);

监听多个model

通过Mixin进行混入。

class CounterModel extends Model with ClickModel{}

虽然有效果,但是编辑器提示错误:说不能进行 Mixin。这个问题待解决

The class ‘ClickModel’ can’t be used as a mixin because it extends a class other than Object.


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!