flutter 新状态管理方案 Provide 使用

文章目录

开这篇文章是因为看到这个库被托管在google的仓库下,而且说明是被设计出来替代ScopedModel的,而且更加灵活

支持Builder模式和StreamBuilder模式,全局,局部都可以

内部应该是结合InheritedWidget Notification体系实现的

传统的bloc需要在每一个Repository中创建StreamControllerStream,甚至有的文章中,一个监听的修改需要修改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仓库下,个人觉得应该是官方很推荐的一种状态管理模式

欢迎大家入手