本身寫一個Provider

在之前我寫過一篇文章,教你如何實現FlutterBLoC框架(juejin.im/post/5cb80c… ),這個BLoC的實現,模仿android開發中的MVVM開發方式,利用FlutterStream,在數據改變的時候,由Stream推送數據給UI層,而後UI層自動更新UI。此次咱們來本身實現一個Provider,這個也是Flutter中最經常使用的狀態管理框架,由咱們本身實現Provider,來了解Provider內部核心原理。android

首先,在上一篇文章中(InheritWidget原理解析:juejin.im/post/5edb97… ),咱們查看輸出日誌發現一個問題,就是點擊「數字+1」這個按鈕的時候,「數字+1」這個按鈕也刷新了(日誌輸出CustomRaisedButton build),這是由於咱們調用的setState方法是BodyWidgetState這個類的方法,因此BodyWidget和它的child widget都重建了,而咱們的需求是InheritedWidget的數據改變的時候只刷新依賴此InheritedWidgetwidget,要作到這一點,咱們就不能調用BodyWidgetState的setState方法,而只調用InheritedWidget上一個節點的setState,也就是說要把InheritedWidget做爲一個StatefulWidgetchild。而後咱們分步驟編寫代碼:緩存

  1. 建立可被監聽的數據管理類,爲何咱們不直接讓InheritedWidget成爲可被監聽的類型呢?這是由於InheritedWidget只是提供數據,數據的消費者應該持有的是數據而不是InheritedWidget,不然數據消費者若是持有InheritedWidget的話,修改具體數據的方法就要添加到InheritedWidget裏,而數據類型是多種多樣的,不可能所有寫到InheritedWidget裏,因此咱們要建立可被監聽的數據管理類,這樣當數據發生變化的時候,調用數據管理類的方法,數據管理類再通知該類的監聽者
  2. 建立InheritedWidget子類,用來保存數據管理類,該類能夠在重建之後通知全部依賴了該類的widget
  3. 建立StatefulWidget,這裏咱們起名爲ProviderCreator,用來保存實際顯示的Widget和可被監聽的數據管理類,並根據二者建立InheritedWidget,並監聽數據管理類的變化。這樣能夠在數據管理類中的數據發生變化的時候能夠經過ProviderCreator對應的State調用setState來讓InheritedWidget重建。InheritedWidget重建時若是ProviderCreator對應的Element沒有被銷燬的話,那這個ProviderCreator內部的可被監聽的數據管理類和實際顯示的child就被緩存起來了(注意:這個child是咱們傳入的實際顯示的widget,而不是ProviderCreator對應的Statebuild方法裏返回的widget

開始咱們的代碼編寫bash

1. 建立可被監聽的數據管理類

建立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();
      }
    }
  }
}
複製代碼

代碼我簡化了一下,就是上面的樣子,咱們的可被監聽的數據類都須要繼承該類框架

2. 建立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;
  }
}
複製代碼

3. 建立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了,注意:Providerof方法中的dependOn參數,爲true說明調用了該方法的widget依賴了InheritedWidget,爲false就沒有依賴InheritedWidget,具體的能夠看dependOnInheritedWidgetOfExactTypegetElementForInheritedWidgetOfExactType這兩個方法的源碼,這裏再也不贅述。post

至此,咱們作了一個簡單的Provider,大功告成!測試

相關文章
相關標籤/搜索