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倉庫下,我的以爲應該是官方很推薦的一種狀態管理模式