(转)flutter 新状态管理方案 Provide (一)-使用


flutter 新状态管理方案 Provide (一)-使用

   

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

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

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

传统的bloc需要在每一个Repository中创建StreamControllerStream,甚至有的文章中,一个监听的修改需要修改5处,维护起来比较麻烦

相比较而言Provide维护起来会稍微省事一些

入门级仓库地址

添加依赖

查看pub-install

dependencies:
  provide: ^1.0.1 # 这里的版本查看官方
 

flutter packages get

import 'package:provide/provide.dart';

使用方法

这里以简单的Counter为例
也就是在flutter的hello world工程的基础上来修改

1. 定义一个Model

这个model需要继承ChangeNotifier

class Counter with ChangeNotifier {
  int _value;

  int get value => _value;

  Counter(this._value);

  void inc() {
    _value++;
    notifyListeners(); //父类的方法,发出通知
  }
}

2. 定义一个全局的Provide

这里虽然定义在全局,但事实上也可以定义在页面级

void main() {
  var providers = Providers()..provide(Provider.function((ctx) => Counter(0)));

  runApp(
    ProviderNode(
      child: MyApp(),
      providers: providers,
    ),
  );
}

 

 

 

ProviderNode表示的是提供者

3. 界面/监听

修改_MyHomePageState

添加一个方法,用于获取Counter实例

Counter get _counter => Provide.value<Counter>(context);
 

将原来的Text(_counter)修改一下

这里的Provide会在Counter发生变化的时候,触发builder回调来更新界面

Provide<Counter>(
    builder: (BuildContext context, Widget child, Counter counter) {
        return Text(
            '${counter.value}',
            style: Theme.of(context).textTheme.display1,
        );
    },
),

 

 

 

4. 发出通知

接着就是发出通知了

修改floatingActionButton的点击事件

floatingActionButton: FloatingActionButton(
  onPressed: () => _counter.inc(),
  tooltip: 'Increment',
  child: Icon(Icons.add),
),
 

 

这里调用第三步获取的那个Counter,然后调用inc方法


看到这里,如果之前用过ScopedModel的朋友会问了,这个不是和以前一样吗,我为啥要改呢

继续修改

5. Stream模式

这个就很类似于bloc了,只不过model不太一样

添加一个StreamBuilder

StreamBuilder<Counter>(
  initialData: _counter,
  stream: Provide.stream<Counter>(context),
  builder: (BuildContext context, AsyncSnapshot<Counter> snapshot) {
    return Text(
      '${snapshot.data.value}',
      style: Theme.of(context).textTheme.display1,
    );
  },
),
 

 

这里initialData是第三步创建的那个,stream是使用Provide.stream<Counter>(context)获取的

scope

provide中有一个概念叫scope,类的完整类名叫ProviderScope

class ProviderScope {
  final String _name;

  /// Constructor
  const ProviderScope(this._name);

  @override
  String toString() {
    return "Scope ('$_name')";
  }
}
 

 

这个类的作用就是标识Provider的区域,或者可以理解为给Provider/Provide定义一个作用区域

只有scope相同的才可以识别

将state的代码修改一下

class _MyHomePageState extends State<MyHomePage> {
  Counter get _counter => Provide.value<Counter>(context);

  PageCounter pageCounter = PageCounter(0);
  PageCounter pageCounter2 = PageCounter(0);
  var scope1 = ProviderScope("1"); 
  var scope2 = ProviderScope("2");
  @override
  Widget build(BuildContext context) {
    return ProviderNode(
      providers: Providers()
        ..provide(Provider.value(pageCounter), scope: scope1)
        ..provide(Provider.value(pageCounter2), scope: scope2),
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              Provide<PageCounter>(
                scope: scope1,
                builder:
                    (BuildContext context, Widget child, PageCounter counter) {
                  return Text(
                    '${counter.value}',
                    style: Theme.of(context).textTheme.display1,
                  );
                },
              ),
              Provide<PageCounter>(
                scope: scope2,
                builder:
                    (BuildContext context, Widget child, PageCounter counter) {
                  return Text(
                    '${counter.value}',
                    style: Theme.of(context).textTheme.display1,
                  );
                },
              ),
              StreamBuilder<Counter>(
                initialData: _counter,
                stream: Provide.stream<Counter>(context),
                builder:
                    (BuildContext context, AsyncSnapshot<Counter> snapshot) {
                  return Text(
                    '${snapshot.data.value}',
                    style: Theme.of(context).textTheme.display1,
                  );
                },
              ),
              FlatButton(
                child: Text("nextPage"),
                onPressed: () {
                  Navigator.push(context,
                      MaterialPageRoute(builder: (BuildContext context) {
                    return MyHomePage(
                      title: "new page",
                    );
                  }));
                },
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            _counter.inc();
            pageCounter.inc();
            pageCounter2.rec();
          },
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

 

 

 

这里定义了两个scope,并在Provide时进行了指定

Provide<PageCounter>(
  scope: scope1,
  builder:
      (BuildContext context, Widget child, PageCounter counter) {
    return Text(
      '${counter.value}',
      style: Theme.of(context).textTheme.display1,
    );
  },
),
 

 

这样只有当对应scope1的counter发出通知时,这里才会回调,这样就满足了一个页面/一个应用中有两个相同对象的识别问题

后记

这个插件托管在google仓库下,个人觉得应该是官方很推荐的一种状态管理模式

 

上一篇:浅谈vue中provide和inject 用法


下一篇:设计模式(三)Singleton Pattern单例设计模式