在之前我寫過一篇文章,教你如何實現Flutter
的BLoC
框架(juejin.im/post/5cb80c… ),這個BLoC
的實現,模仿android
開發中的MVVM
開發方式,利用Flutter
的Stream
,在數據改變的時候,由Stream
推送數據給UI
層,而後UI
層自動更新UI
。此次咱們來本身實現一個Provider
,這個也是Flutter
中最經常使用的狀態管理框架,由咱們本身實現Provider
,來了解Provider
內部核心原理。android
首先,在上一篇文章中(InheritWidget
原理解析:juejin.im/post/5edb97… ),咱們查看輸出日誌發現一個問題,就是點擊「數字+1」這個按鈕的時候,「數字+1」這個按鈕也刷新了(日誌輸出CustomRaisedButton build
),這是由於咱們調用的setState
方法是BodyWidgetState
這個類的方法,因此BodyWidget
和它的child widget
都重建了,而咱們的需求是InheritedWidget
的數據改變的時候只刷新依賴此InheritedWidget
的widget
,要作到這一點,咱們就不能調用BodyWidgetState的setState
方法,而只調用InheritedWidget
上一個節點的setState
,也就是說要把InheritedWidget
做爲一個StatefulWidget
的child
。而後咱們分步驟編寫代碼:緩存
InheritedWidget
成爲可被監聽的類型呢?這是由於InheritedWidget
只是提供數據,數據的消費者應該持有的是數據而不是InheritedWidget
,不然數據消費者若是持有InheritedWidget
的話,修改具體數據的方法就要添加到InheritedWidget
裏,而數據類型是多種多樣的,不可能所有寫到InheritedWidget
裏,因此咱們要建立可被監聽的數據管理類,這樣當數據發生變化的時候,調用數據管理類的方法,數據管理類再通知該類的監聽者InheritedWidget
子類,用來保存數據管理類,該類能夠在重建之後通知全部依賴了該類的widget
StatefulWidget
,這裏咱們起名爲ProviderCreator
,用來保存實際顯示的Widget
和可被監聽的數據管理類,並根據二者建立InheritedWidget
,並監聽數據管理類的變化。這樣能夠在數據管理類中的數據發生變化的時候能夠經過ProviderCreator
對應的State
調用setState
來讓InheritedWidget
重建。InheritedWidget
重建時若是ProviderCreator
對應的Element
沒有被銷燬的話,那這個ProviderCreator
內部的可被監聽的數據管理類和實際顯示的child
就被緩存起來了(注意:這個child
是咱們傳入的實際顯示的widget
,而不是ProviderCreator
對應的State
的build
方法裏返回的widget
)開始咱們的代碼編寫bash
建立InheritedWidget
子類的時候,裏面的數據能夠是任意類型,可是咱們須要在數據改變的時候通知監聽者,因此咱們約束一下里面的數據類型必須是可被監聽的類型,在flutter
裏,有一個類叫作ChangeNotifier
,很是適合用來做爲被監聽者,代碼以下app
class ChangeNotifier implements Listenable {
ObserverList<VoidCallback> _listeners = ObserverList<VoidCallback>();
@protected
bool get hasListeners {
return _listeners.isNotEmpty;
}
@override
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
@override
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
@mustCallSuper
void dispose() {
_listeners = null;
}
@protected
@visibleForTesting
void notifyListeners() {
if (_listeners != null) {
final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners);
for (final VoidCallback listener in localListeners) {
listener();
}
}
}
}
複製代碼
代碼我簡化了一下,就是上面的樣子,咱們的可被監聽的數據類都須要繼承該類框架
InheritedWidget
子類由於該類用來提供可被監聽的數據管理類,因此起名叫`Provider`
複製代碼
class Provider<T extends ChangeNotifier> extends InheritedWidget {
final T data;
Provider({Key key, this.data, Widget child}) : super(key: key, child: child) {
print("Provider=$hashCode");
}
//定義一個便捷方法,方便子樹中的widget獲取共享數據
static Provider<T> of<T extends ChangeNotifier>(BuildContext context, bool dependOn) {
if (dependOn) {
return context.dependOnInheritedWidgetOfExactType<Provider<T>>();
} else {
return context.getElementForInheritedWidgetOfExactType<Provider<T>>().widget as Provider<T>;
}
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
//在此簡單返回true,則每次更新都會調用依賴其的子孫節點的'didChangeDependencies'。
return true;
}
}
複製代碼
StatefulWidget
,用來保存實際顯示的Widget
和可被監聽的數據管理類,並根據二者建立InheritedWidget
,並監聽數據管理類的變化。class ProviderCreator<T extends ChangeNotifier> extends StatefulWidget {
final T data;
final Widget child;
ProviderCreator({
Key key,
this.data,
this.child,
}) {
print("ProviderCreator=$hashCode");
}
@override
State<StatefulWidget> createState() {
return ProviderCreatorState<T>();
}
}
class ProviderCreatorState<T extends ChangeNotifier> extends State<ProviderCreator> {
void update() {
setState(() {});
}
@override
void initState() {
widget.data.addListener(update);
super.initState();
}
@override
void dispose() {
widget.data.dispose();
super.dispose();
}
@override
void didUpdateWidget(ProviderCreator<ChangeNotifier> oldWidget) {
//當Provider更新時,若是新舊數據不"==",則解綁舊數據監聽,同時添加新數據監聽
if (oldWidget.data != widget.data) {
oldWidget.data.dispose();
widget.data.addListener(update);
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
print("""CustomInheritedWidgetCreatorState build \twidget=${widget.hashCode} \twidget.data.hashCode=${widget.data.hashCode} \twidget.child=${widget.child.hashCode}""");
return Provider<T>(
data: widget.data,
child: widget.child,
);
}
}
複製代碼
而後,咱們用一個示例來試一下咱們剛纔本身寫的Provider
咱們還用上篇文章裏的示例,用一個計數器程序來測試。less
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter InheritWidget',
home: Scaffold(
appBar: AppBar(),
body: Center(
child: BodyWidget(),
),
),
);
}
}
class BodyWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return BodyWidgetState();
}
}
class BodyWidgetState extends State<BodyWidget> {
Counter counter = Counter();
@override
Widget build(BuildContext context) {
return ProviderCreator<Counter>(
data: counter,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
//依賴
DependOnInheritedWidget<Counter>(),
//不依賴
Builder(builder: (context) {
return Text(Provider.of<Counter>(context, false).data.toString());
}),
Builder(builder: (context) {
return CustomRaisedButton(
onPressed: () {
//不依賴
Provider.of<Counter>(context, false).data.increment();
},
child: Text("數字+1"),
);
})
],
),
);
}
}
class Counter extends ChangeNotifier {
int num = 0;
void increment() {
num++;
notifyListeners();
}
@override
String toString() {
return "$num";
}
}
class DependOnInheritedWidget<T extends ChangeNotifier> extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return DependOnInheritedWidgetState<T>();
}
}
class DependOnInheritedWidgetState<T extends ChangeNotifier> extends State<DependOnInheritedWidget> {
@override
Widget build(BuildContext context) {
print("DependOnInheritedWidgetState build");
return Text(Provider.of<T>(context, true).data.toString());
}
@override
void didChangeDependencies() {
print("DependOnInheritedWidgetState didChangeDependencies");
super.didChangeDependencies();
}
}
class CustomRaisedButton extends RaisedButton {
const CustomRaisedButton({
@required VoidCallback onPressed,
Widget child,
}) : super(onPressed: onPressed, child: child);
@override
Widget build(BuildContext context) {
print("CustomRaisedButton build");
return super.build(context);
}
}
複製代碼
當咱們點擊「數字+1」這個按鈕的時候,會在日誌打印裏發現以下信息:ide
I/flutter ( 489): CustomInheritedWidgetCreatorState build
I/flutter ( 489): widget=136741630
I/flutter ( 489): widget.data.hashCode=597399651
I/flutter ( 489): widget.child=443053943
I/flutter ( 489): Provider=611638398
I/flutter ( 489): DependOnInheritedWidgetState didChangeDependencies
I/flutter ( 489): DependOnInheritedWidgetState build
複製代碼
說明CustomRaisedButton
再也不build
了,注意:Provider
的of
方法中的dependOn
參數,爲true
說明調用了該方法的widget
依賴了InheritedWidget
,爲false
就沒有依賴InheritedWidget
,具體的能夠看dependOnInheritedWidgetOfExactType
和getElementForInheritedWidgetOfExactType
這兩個方法的源碼,這裏再也不贅述。post
至此,咱們作了一個簡單的Provider
,大功告成!測試