provider基於觀察者(釋出-訂閱)模式。
provider 的組成以及作用:
Listenable
作為釋出者的角色,需要和Provider配合使用,相關元件有:
- Listenable 抽象類,需要自己繼承,實現addListener removeListener 函式 以及 維護listeners列表。
- ChangeNotifier 繼承於Listenable 內部提供實現好的釋出者功能,常於ChangeNotifierProvider相結合使用。
- ValueNotifier 繼承於ChangeNotifier,和ValueListenableProvider配合使用, 區別在於,內部提供了單一變數的狀態變化自動通知,不需要自行呼叫notifyListeners()。缺點在於犧牲了靈活性,僅對於內部value的set方法做了監聽。
Consumer ConsumerX
作為訂閱者的角色,也是一個便捷構造器,內部幫我們呼叫Provider.of(context)
來獲取ChangeNotifier
,以及將將自身加入訂閱者中。
也可以不使用Consumer,直接在widget中使用Provder.of(conext) 是一樣的效果。
Provider
Provider 基於一個DelegateWidget來實現,它提供一對creat和dispose,來讓我們管理資料的生命週期,所以通過Provider我們不用擔心記憶體釋放以及資料請求等問題(這種場景下完全可以替代StatefulWidget)。
除此之外,Provider還提供了不同的子類來適應各種場景:
- Provider :不僅提供creat和dispose,還擁有Inherited特性,它繼承於InheritedProvider,其內部基於InheritedWidget實現。但是Provider並不具備重新整理子部件的功能,僅能用於提供固定資料。
- ListenableProvider :一個偏底層且擁有重新整理子部件功能的Provider,需要提供一個繼承於Listenable的釋出者進行訊息釋出。
- ChangeNotifierProvider : 繼承於ListenableProvider,需要和ChangeNotifier結合使用:
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<CustomChangeNotifier>(
create: (BuildContext context) {
return CustomChangeNotifier();
},
child: Consumer<CustomChangeNotifier>(
builder: (builder: (context, notifier, _) {
return new Column(
children: <Widget>[
new Expanded(child: new Center(child: new Text(notifier.count.toString()))),
new Center(
child: new FlatButton(
onPressed: () {
notifier.add();
},
color: Colors.blue,
child: new Text("+")),
)
],
);
}
複製程式碼
- FutureProvider:支援非同步,需要提供一個Future,等待Future完成後,自動重新整理子部件。
- StreamProvider:支援非同步,需要提供一個Steam充當釋出者,當有流輸入後,重新整理子部件。
- ValueListenableProvider : 支援單一變數,需要提供一個ValueNotifier對資料進行包裝,
- MultiProvider 針對Provider進行組合:
void main() {
final counter = CounterModel();
final textSize = 48;
runApp(
MultiProvider(
providers: [
Provider.value(value: textSize),
ChangeNotifierProvider.value(value: counter)
],
child: MyApp(),
),
);
}
複製程式碼
- ProxyProvider ProxyProviderX :結合MultiProvider可以相應其他Provider的更新:
比如一個圖片上傳,我們可以通過一個ChangeNotifierProvider進行圖片上傳獲取URL,然後通過ProxyProvider攔截上傳後的URL,把上傳到伺服器中。
/// 官方示例
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
ProxyProvider<Counter, Translations>(
update: (_, counter, __) => Translations(counter.value),
),
],
child: Foo(),
);
}
class Translations {
const Translations(this._value);
final int _value;
String get title => 'You clicked $_value times';
}
複製程式碼
Selector
在業務層的細粒度重新整理下,光靠Provider控制是遠遠不夠的,這時候,就需要Selector。 Selector和Consumer扮演一樣的角色—訂閱者,使用起來也和Consummer差不多,只不過多一個selector。
實現原理:
Selector繼承於SingleChildStatefulWidget,為了防止重複構建,重寫了buildWithChild並對widget進行了快取,通過selector選擇器傳回來的資料進行equals判斷,當然還有_shouldRebuild可以進行強制構建。
如何使用:
Selector接受兩個泛型變數 Selector<T, S>,通過Provider.of(context),獲取釋出者,同時S會作為檢視是否需要重新構建的資料型別。
Selector<Translations, String>(
selector: (context, provider) {
return provider.title; // 將改變後的資料返回
},
builder: (context, value, child) {
print("重新整理了");
return Text("$value"); // 重新構建
},
),
複製程式碼
優化心得:
區域性重新整理控制
Provider具有非常好的生命週期控制,控制好重新整理範圍,儘量支援最小化原則,這樣不僅對於重新整理速度有所提升,在資料控制方面例如記憶體的申請和釋放頻次,也有更好的優化。
善用懶載入
這裡的懶載入是指的是create的延遲呼叫,通常create中還會進行網路請求、資料初始化等操作,且在provider的整個生命週期中,只呼叫一次,如果想要關閉懶載入,需要將lazy置為false。
Dispose釋放
provider對於記憶體控制很看重,除了各類Provider的dispose支援,ChangeNotifier也提供了dispose,