flutter 新状态管理方案 Provide (一)-使用
开这篇文章是由于看到这个库被托管在google的仓库下,并且说明是被设计出来替代ScopedModel
的,并且更加灵活java
支持Builder模式和StreamBuilder模式,全局,局部均可以git
内部应该是结合InheritedWidget
Notification
体系实现的github
传统的bloc须要在每个Repository
中建立StreamController
和Stream
,甚至有的文章中,一个监听的修改须要修改5处,维护起来比较麻烦markdown
相比较而言Provide
维护起来会稍微省事一些app
入门级仓库地址ide
添加依赖
查看pub-installpost
dependencies: provide: ^1.0.1 # 这里的版本查看官方
flutter packages getui
import 'package:provide/provide.dart';
使用方法
这里以简单的Counter为例
也就是在flutter的hello world工程的基础上来修改this
1. 定义一个Model
这个model须要继承ChangeNotifier
google
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仓库下,我的以为应该是官方很推荐的一种状态管理模式