能夠進行依賴注入和狀態管理,使用widget建立,適用於widget。async
它是故意設計成使用widget來進行依賴注入和狀態管理的,而不是純使用dart類,像是stream這些。由於widget簡單且健壯可伸縮。ide
使用weidget來進行狀態管理能夠保證。函數
除了暴露一個值的可訪問性,provider還包括這個值的建立,監聽,銷燬。測試
爲了暴露一個新建立的對象,可使用provider的默認構造函數。不要使用.value
命名構造函數類建立一個值對象,否則可能會形成其餘不指望影響。fetch
create
中建立一個對象。Provider( create:(_)=>new MyModel(), child:... )
Provider.value
命名構造函數建立一個對象。ChangeNotifierProvider.value( value:new MyModel(), child:... )
若是建立了一個對象,它使用了爲了可能變動的變量作參數,那麼考慮使用ProxyProvider:ui
int count; ProxyProvider0( update:L(_,__)=> new MyModel(count), child:... )
若是有個對象實例,你想把其暴露在其餘地方使用,那麼你應該使用Provider的.value
命名構造函數。this
不這麼作可能會形成在該對象還在使用時被dispose
。設計
ChangeNotifierProvider.value
來提供一個已經存在的ChangeNotifier
。MyChangeNotifier varibale; ChangeNotifierProvider.value( value:variable, child:... )
CahangeNotifier
。MyChangeNotifier variable; ChangeNotifierProvider( create:(_)=>variable, child:... )
獲取一個值最簡單的方法是使用靜態方法Provider.of<T>(BuildContext context)
。code
這個方法會從當前的context在widget樹中向根widget方向查找符合類型T的最近的值。(若是沒有找到就throw)。對象
除了Provider.of
方法咱們也可使用Consumer
和Selector
兩個widget。
這對於高效的組織代碼以及難以獲取BuildContext
的狀況比較有幫助。
當在一個較大的應用中注入較多的數據時,Provider會飛快地嵌套多層。
Provider<Something>( create:(_)=>Something(), child: Provider<SomethingElse>( create:(_)=>SomethingElse(), child:Provider<AnotherThing>( create:(_)=>AnotherTing(), child:someWidget, ) ) )
能夠這樣寫
MultiProvider( Providers:[ Provider<Somthing>(create:(_)=>Something()), Provider<SomthingElse>(create:(_)=>SomethingElse()), Provider<AnotherThing>(create:(_)=>AnotherTthing()), ], child:someWidget )
上面代碼的結果是嚴格的同樣的。MultiProvider
僅僅是改變了代碼的形式。
從版本3.0.0開始增長了一個新的Provider:ProxyProvider。
ProxyProvider自己是一個Provider,它把其餘多個provider的數據結合成一個新的對象,而且把這個結果發送一個一個Provider。
被結合的的這些provider中的任何一個數據更新了,這個新的對象都會更新。
下面這個例子使用了ProxyProvider,他把其餘provider中的counter作了箇中轉。
Widget build(BuildContext context){ return MultiProvider( providers:[ ChangeNotifierProvider(create:(_)=>Counter()), ProxyProvider<Counter,Translations>( create:(_,counter,__)=>Translations(clunter.value), ), child:Foo() ] ) } class Translations{ const Translations(this._value); final init _value; String get title=>'You clicked: $_value times'; }
ProxyProvider有不少種變體,例如:
ProxyProvider
vs ProxyProvider2
vs ProxyProvider3
...類名後的數字是指ProxyProvider
依賴其餘Provider的數量。
ProxyProvider
vs ChangeNotifierProxyProvider
vs ListenableProxyProvider,...他們的工做方式是相似的,相對於發送結果給一個Provider,一個ChangeNotifierProxyProvider
會發送給一個ChangeNotifierProxyProvider
。這個錯誤是由於你想監聽一個在其生命週期中不會被再次調用的provider。
這說明你不會再使用其餘的生命週期(didChangeDependencies/build),或者你不在意數據更新。
不要這麼作
initState(){ super.initState(); print(Provider.of<Foo>(context).value); }
你能夠這麼作
Value value; didChangeDependencies(){ super.didChangeDependencies(); final value = Provider.of<Foo>(context).value; if(value != this.value){ this.value = value; print(value); } }
每當值發生了變化,都會被打印。
也能夠這麼作
initState(){ super.initState(); print(Provider.of<Foo>(context,listen:false).value); }
這樣只會打印value一次,再也不更新。
ChangeNotifier
,當我更新數據時發生了錯誤,發生了什麼?這個常常發生在widget樹正在構建時,你對ChangeNotifier進行了更改操做。
一個典型的情景是,發起了一個http請求,而後該future被保存在了notifer中。
initState(){ super.initState(); Provider.of<Foo>(context).fetchSomething(); }
這樣是禁止的,由於更改必須是當即的。
這意味着有些widget可能在變更以前build,然而其餘的在變更以後build。這可能會形成你的ui發生衝突,因此是禁止的。
相比,你能夠在整個widget樹都同步以後(渲染前/選而後?)進行變更。
class Mymodel width ChangeNotifier{ MyModel(){ _fetchSomething(); } Future<void> _fetchSomething()async {} }
這個適用於沒有額外參數的狀況。
initState(){ super.initState(); Future.microtash(()=>{ Provider.of<Foo>(context).fetchSomething(someValue); }) }
這個多少是不太理想的,可是容許傳入參數進行變動。
不是。
你可使用任何對象來呈現狀態。例如其餘可用的方式是Provider.value()結合一個StatefulWidget使用。
這裏有個計數的例子,使用了這個方法:
class Example extends StatefulWidget{ const Example({Key key, this.child}):super(key key); final Widget child; @override ExampleState createState()=> ExampleState(); } class ExampleState extends State<Example>{ int _count; void increment(){ setState((){ _count++; }) } @override Widget build(BuildContext context){ return Provider.value( value:_count, child:Provider.value( value:this, child:widget.child ) ) } }
能夠如此讀取數據:
return Text(Provider.of<int>(context).toString());
如此更改數據:
return FloatingActionButton( onPress:Provider.of<Examp0leState>(context).increment, child:Icon(Icons.plus_one), );
此外,你也能夠建立本身的provider。
固然,provider暴露了全部的小的組件,這些製做了一個簡陋的provider。
包括:
相較於Provider.of,你可使用Consumer/Selector。
他們可選的child參數只容許重建widget中很是小的具體部分。
Foo( child:Consumer<A>( builder:(_,a,child){ return Bar(a:a,child:child); } child: Baz(), ), )
這個例子中只有Bar會在A更新時被重建,Foo和Baz非必要下不會更新。
更深一步,使用selector來忽略widget樹中一些沒有影響的更新也是可能的。
Selector<List,int>( selector:(_,list)=>list.length, builder:(_,length,__){ return Text('$length'); } );
這個代碼片斷中,只有list的length變化時,纔會被重構。即便一個item發生了變化也不會更新。
不能。
你可使用多個Provider共享一樣的類型,一個widget只能獲取到他們中的一個:最近的那個。
否則,你必須給與不一樣的provider不一樣的數據類型。
相較:
Provider<String>( create:(_)=>'england', child:Provider<String>( create:(_)=>'London', child:... ) )
推薦:
Provider<Country>( create:(_)=>'england', child:Provider<City>( create:(_)=>'London', child:... ) )
provider包提供了一些不一樣類新的'provider'應對不一樣類型的對象。
以下:
名字 | 說明 |
---|---|
Provider | provider的最基本形式,能夠添加和暴露任何形式的值 |
ListenableProvider | 使用Listenable對象的特殊provider,ListenableProvider會監聽對象,而且在監聽器在任什麼時候候調用時要求widget重構。 |
ChangeNotifierProvider | ChangeNotifier規格的ListenableProvider,他會在必要時自動調用ChangeNotifier.dispose. |
ValueListenableProvider | 監聽一個ValueListenable而且只暴露ValueListenable.value。 |
StreamProvider | 監聽Stream而且暴露最新的emitted的值。 |
FutureProvider | 添加一個Future,而且在future完成時更新附從。 |