flutter 新状态管理方案 Provide 使用
文章目录
开这篇文章是因为看到这个库被托管在google的仓库下,而且说明是被设计出来替代ScopedModel
的,而且更加灵活
支持Builder模式和StreamBuilder模式,全局,局部都可以
内部应该是结合InheritedWidget
Notification
体系实现的
传统的bloc需要在每一个Repository
中创建StreamController
和Stream
,甚至有的文章中,一个监听的修改需要修改5处,维护起来比较麻烦
相比较而言Provide
维护起来会稍微省事一些
添加依赖
查看
1dependencies:
2 provide: ^1.0.1 # 这里的版本查看官方
1flutter packages get
1import 'package:provide/provide.dart';
使用方法
这里以简单的Counter为例 也就是在flutter的hello world工程的基础上来修改
1. 定义一个Model
这个model需要继承ChangeNotifier
1
2class Counter with ChangeNotifier {
3 int _value;
4
5 int get value => _value;
6
7 Counter(this._value);
8
9 void inc() {
10 _value++;
11 notifyListeners(); //父类的方法,发出通知
12 }
13}
2. 定义一个全局的Provide
这里虽然定义在全局,但事实上也可以定义在页面级
1void main() {
2 var providers = Providers()..provide(Provider.function((ctx) => Counter(0)));
3
4 runApp(
5 ProviderNode(
6 child: MyApp(),
7 providers: providers,
8 ),
9 );
10}
ProviderNode
表示的是提供者
3. 界面/监听
修改_MyHomePageState
添加一个方法,用于获取Counter实例
1Counter get _counter => Provide.value<Counter>(context);
将原来的Text(_counter)修改一下
这里的Provide会在Counter发生变化的时候,触发builder回调来更新界面
1Provide<Counter>(
2 builder: (BuildContext context, Widget child, Counter counter) {
3 return Text(
4 '${counter.value}',
5 style: Theme.of(context).textTheme.display1,
6 );
7 },
8),
4. 发出通知
接着就是发出通知了
修改floatingActionButton的点击事件
1floatingActionButton: FloatingActionButton(
2 onPressed: () => _counter.inc(),
3 tooltip: 'Increment',
4 child: Icon(Icons.add),
5),
这里调用第三步获取的那个Counter
,然后调用inc方法
看到这里,如果之前用过ScopedModel的朋友会问了,这个不是和以前一样吗,我为啥要改呢
继续修改
5. Stream模式
这个就很类似于bloc了,只不过model不太一样
添加一个StreamBuilder
1StreamBuilder<Counter>(
2 initialData: _counter,
3 stream: Provide.stream<Counter>(context),
4 builder: (BuildContext context, AsyncSnapshot<Counter> snapshot) {
5 return Text(
6 '${snapshot.data.value}',
7 style: Theme.of(context).textTheme.display1,
8 );
9 },
10),
这里initialData
是第三步创建的那个,stream是使用Provide.stream<Counter>(context)
获取的
scope
在provide
中有一个概念叫scope,类的完整类名叫ProviderScope
1class ProviderScope {
2 final String _name;
3
4 /// Constructor
5 const ProviderScope(this._name);
6
7 @override
8 String toString() {
9 return "Scope ('$_name')";
10 }
11}
这个类的作用就是标识Provider的区域,或者可以理解为给Provider
/Provide
定义一个作用区域
只有scope相同的才可以识别
将state的代码修改一下
1class _MyHomePageState extends State<MyHomePage> {
2 Counter get _counter => Provide.value<Counter>(context);
3
4 PageCounter pageCounter = PageCounter(0);
5 PageCounter pageCounter2 = PageCounter(0);
6 var scope1 = ProviderScope("1");
7 var scope2 = ProviderScope("2");
8 @override
9 Widget build(BuildContext context) {
10 return ProviderNode(
11 providers: Providers()
12 ..provide(Provider.value(pageCounter), scope: scope1)
13 ..provide(Provider.value(pageCounter2), scope: scope2),
14 child: Scaffold(
15 appBar: AppBar(
16 title: Text(widget.title),
17 ),
18 body: Center(
19 child: Column(
20 mainAxisAlignment: MainAxisAlignment.center,
21 children: <Widget>[
22 Text(
23 'You have pushed the button this many times:',
24 ),
25 Provide<PageCounter>(
26 scope: scope1,
27 builder:
28 (BuildContext context, Widget child, PageCounter counter) {
29 return Text(
30 '${counter.value}',
31 style: Theme.of(context).textTheme.display1,
32 );
33 },
34 ),
35 Provide<PageCounter>(
36 scope: scope2,
37 builder:
38 (BuildContext context, Widget child, PageCounter counter) {
39 return Text(
40 '${counter.value}',
41 style: Theme.of(context).textTheme.display1,
42 );
43 },
44 ),
45 StreamBuilder<Counter>(
46 initialData: _counter,
47 stream: Provide.stream<Counter>(context),
48 builder:
49 (BuildContext context, AsyncSnapshot<Counter> snapshot) {
50 return Text(
51 '${snapshot.data.value}',
52 style: Theme.of(context).textTheme.display1,
53 );
54 },
55 ),
56 FlatButton(
57 child: Text("nextPage"),
58 onPressed: () {
59 Navigator.push(context,
60 MaterialPageRoute(builder: (BuildContext context) {
61 return MyHomePage(
62 title: "new page",
63 );
64 }));
65 },
66 ),
67 ],
68 ),
69 ),
70 floatingActionButton: FloatingActionButton(
71 onPressed: () {
72 _counter.inc();
73 pageCounter.inc();
74 pageCounter2.rec();
75 },
76 tooltip: 'Increment',
77 child: Icon(Icons.add),
78 ),
79 ),
80 );
81 }
82}
这里定义了两个scope
,并在Provide时进行了指定
1Provide<PageCounter>(
2 scope: scope1,
3 builder:
4 (BuildContext context, Widget child, PageCounter counter) {
5 return Text(
6 '${counter.value}',
7 style: Theme.of(context).textTheme.display1,
8 );
9 },
10),
这样只有当对应scope1的counter发出通知时,这里才会回调,这样就满足了一个页面/一个应用中有两个相同对象的识别问题
后记
这个插件托管在google仓库下,个人觉得应该是官方很推荐的一种状态管理模式
欢迎大家入手