前言
當(dāng)應(yīng)用中需要一些跨組件(包括跨路由)的狀態(tài)需要同步時百框,如果不引入狀態(tài)管理技術(shù)的話。能想到的辦法就是將狀態(tài)進(jìn)行提升寡具,放到其共同的父節(jié)點(diǎn)上秤茅,然后將父節(jié)點(diǎn)設(shè)計為有狀態(tài)組件,并提供修改狀態(tài)的方法給到子組件童叠。
根據(jù)生命周期框喳,可以得知父組件的變化會引發(fā)子組件的 build ,就算子組件沒有任何的改動厦坛。但這樣子因?yàn)橐粋€操作五垮,需要將所有子組件進(jìn)行重新 build,成本實(shí)在太高杜秸。(在 React 中可以通過 shouldComponentUpdate 來“阻止”渲染放仗,進(jìn)行性能優(yōu)化,但在 Flutter 中是沒有該功能來減少重新 build 的)
下面有兩種方法撬碟,第一個是原生所使用的 InheritedWidget诞挨,第二個是官網(wǎng)推薦使用的技術(shù) Provider 莉撇。
(InheritedWidget 在多頁面間數(shù)據(jù)共享比較麻煩,因?yàn)樾枰粋€共同的父節(jié)點(diǎn))
InheritedWidget
InheritedWidget 是 Flutter 中非常重要的一個功能型組件亭姥,它提供了一種數(shù)據(jù)在 widget 樹中從上到下傳遞稼钩、共享的方式。(InheritedWidget 和 React 中的 context 功能類似达罗,和逐級傳遞數(shù)據(jù)相比坝撑,它們能實(shí)現(xiàn)組件跨級傳遞數(shù)據(jù))
創(chuàng)建一個狀態(tài)管理類組件 ChestnutWidget:
class ChestnutWidget extends InheritedWidget {
// 共享狀態(tài)
final String data;
// 修改共享狀態(tài)方法
final Function onDataChange;
// 生成構(gòu)造函數(shù)
ChestnutWidget({
Key key,
@required Widget child,
@required this.data,
@required this.onDataChange,
}) : super(key: key, child: child);
@override
bool updateShouldNotify(ChestnutWidget old) {
//判斷依賴的 data 狀態(tài)是否變化
return old.data != data;
}
}
使用時,在父節(jié)點(diǎn)上粮揉,將所有子節(jié)點(diǎn)作為該節(jié)點(diǎn)狀態(tài)管理類的一個參數(shù)傳遞給 InheritedWidget巡李。
有點(diǎn)類似于 React 中的高階組件(高階組件是參數(shù)為組件,返回值為新組件的函數(shù))扶认。
//...
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ChestnutWidget(
child: childWidget, //子組件
onDataChange: onDataChange,
data: data
),
],
);
}
子節(jié)點(diǎn)通過 dependOnInheritedWidgetOfExactType 方法獲取 InheritedWidget 中的屬性以及方法侨拦。
context.dependOnInheritedWidgetOfExactType<ShareDataWidget>()
如果不希望共享數(shù)據(jù)改變時,調(diào)用 didChangeDependencies 回調(diào)辐宾,導(dǎo)致 build 的話狱从。用 getElementForInheritedWidgetOfExactType 方法獲取 InheritedWidget 中的屬性。
context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget
調(diào)用dependOnInheritedWidgetOfExactType() 和 getElementForInheritedWidgetOfExactType()的區(qū)別就是前者會注冊依賴關(guān)系叠纹,而后者不會季研。
Provider(基于InheritedWidget)
-
ChangeNotifier 是 Flutter SDK 中的一個簡單的類,繼承自 Listenable誉察,它用于向監(jiān)聽器發(fā)送通知与涡。(和觀察者模式相類似)
在 provider 中,ChangeNotifier 是一種能夠封裝應(yīng)用狀態(tài)的方法持偏。
創(chuàng)建一個model:
class ChestnutModel extends ChangeNotifier {
//聲明私有變量
String _name = 'chestnut';
//設(shè)置get方法
String get name => _name;
/// 修改當(dāng)前name
void changeName(String name) {
_name = name
//告訴正在監(jiān)聽此 model 的 widgets 進(jìn)行重新 build
notifyListeners();
}
}
上面代碼中驼卖,和 ChangeNotifier 相關(guān)的代碼就是調(diào)用 notifyListeners(),當(dāng) model 發(fā)生改變并且需要更新 UI 的時候可以調(diào)用該方法鸿秆,其他代碼就是本身的業(yè)務(wù)邏輯酌畜。
- ChangeNotifierProvider widget 可以向其子孫節(jié)點(diǎn)暴露一個 ChangeNotifier 實(shí)例,放在需要訪問它的 widget 之上卿叽。它屬于 provider package桥胞。
通過 ChangeNotifierProvider 讓 ChestnutModel 與 widget 相關(guān)聯(lián):
ChangeNotifierProvider(
create: (context) => ChestnutModel(),
child: childWidget, //子組件
),
如想提供更多狀態(tài),可以使用 MultiProvider
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ChestnutModel()),
Provider(create: (context) => SomeOtherModel()),
],
child: childWidget , //子組件
),
-
Consumer 只是在新的 widget 中調(diào)用 Provider.of附帽,并將其構(gòu)建實(shí)現(xiàn)委派給 builder(builder 不能為null)埠戳。
當(dāng) ChangeNotifier 發(fā)生變化的時候會調(diào)用 builder 這個函數(shù)。(換言之蕉扮,在 model 中調(diào)用 notifyListeners() 時整胃,所有和 Consumer 相關(guān)的 builder 方法都會被調(diào)用)
必須指定要訪問的 model 類型。這里訪問 ChestnutModel 那么就寫上 Consumer<ChestnutModel>:
Consumer<ChestnutModel>(
builder: (context, model,child)=> Stack(
children: [
child,
Text("Name: ${model.name}"),
],
),
)
第一個是 context喳钟,在每個 build 方法中都能找到這個參數(shù)屁使。
第二個參數(shù)是最開始 ChangeNotifier 的實(shí)例在岂。
第三個參數(shù)是 child,用于優(yōu)化目的蛮寂。(當(dāng) model 發(fā)生改變的時候蔽午,該子樹并不會改變)
讀取值的最簡單方法是使用靜態(tài)方法 Provider.of
Provider.of<ChestnutModel>(context).name
有的時候你不需要 model 中的數(shù)據(jù)來改變 UI,但是可能還是需要訪問該數(shù)據(jù)酬蹋。將listen: false 傳遞給 Provider.of及老,當(dāng) notifyListeners 被調(diào)用的時候,并不會使 widget 被重構(gòu)范抓。
Provider.of<ChestnutModel>(context, listen: false).changeName(‘newChestnut’);
學(xué)習(xí)鏈接:
provider文檔
《Flutter實(shí)戰(zhàn)》(7.2 數(shù)據(jù)共享)
Flutter 文檔中文版(簡單的應(yīng)用狀態(tài)管理)