flutter provide 學習筆記

作爲一名前端,關於 flutter 的 狀態管理器 Provide 的使用方法其實不是很能理解,因爲這裏出現了泛型

所以決定去看看 Provide,瞭解使用 Provide 中的泛型到底是怎麼被使用的,同時加深對於 Dart 的理解

首先,是 github 上的 example 源碼,我將會基於這個來進行學習

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:provide/provide.dart';

void main() {
  var counter = Counter();
  Timer.periodic(
    const Duration(seconds: 5),
    (timer) => counter.increment(),
  );
  var providers = Providers();
  providers.provide(Provider<Counter>.value(counter));
  runApp(
    ProviderNode(
      providers: providers,
      child: MyApp(),
    ),
  );
}

/// 這裏定義了一個 狀態管理的 Model 對象
class Counter extends ChangeNotifier {
  int value = 0;

  void increment() {
    value += 1;
    /// 通知 監聽者 去更新數據
    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Provide<Counter>(
              builder: (context, child, counter) => Text(
                    '${counter.value}',
                    style: Theme.of(context).textTheme.display1,
                  ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Provide.value<Counter>(context).increment(),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

首先可以在入口函數裏看到這段代碼

  var counter = Counter();  // 初始化 Counter 
  var providers = Providers();  // 初始化 Providers
  providers.provide(Provider<Counter>.value(counter));
  runApp(
    ProviderNode(
      providers: providers,
      child: MyApp(),
    ),
  );

可以看到 

Provider<Counter>.value(counter)

源碼:

abstract class Provider<T> {
  //  去除了很多 其他的代碼
  T get(BuildContext context);

  Future<void> dispose();
  factory Provider.value(T value) => _ValueProvider(value);

}
// 這裏就 返回了一個註冊好了的數據
class _ValueProvider<T> extends TypedProvider<T> {
  final T _value;
  StreamController _streamController;

  // 其實這裏的 get 還是將你傳進來的數據給返回出去了
  @override
  T get(BuildContext context) => _value;

  @override
  Stream<T> stream(BuildContext context) {
    final value = _value;
    if (value is Listenable) {
      _streamController ??= StreamController<T>.broadcast();
      value.addListener(_streamListener);
    } else {
      throw UnsupportedError(
          'Cannot create stream from a value that is not Listenable');
    }

    return _streamController.stream;
  }

  _ValueProvider(this._value);

 // 註銷,源碼中如果這個 provide 不使用了,就會去調用這裏的 dispose 執行註銷
  @override
  Future<void> dispose() async {
    final value = _value;
    if (value is Listenable) {
      value.removeListener(_streamListener);
    }
    await _streamController?.close();
  }

  void _streamListener() {
    _streamController?.add(_value);
  }
}

而 關於  這裏 數據的收集

  var providers = Providers();
  // 收集  監聽的 數據 見下面
  providers.provide(Provider<Counter>.value(counter));
class Providers {
  /// 同樣刪除了 很多代碼
  Providers();

  // 可以看到,執行這裏 的 provide 實際上就是將 需要監聽的數據都收集起來
  void provide<T>(Provider<T> provider, {ProviderScope scope}) {
    assert(provider.type == T);

    _providersForScope(scope)[T] = provider;
  }
}

接着就是掛載到 根組件上面了

    ProviderNode(
      providers: providers,
      child: MyApp(),
    ),

關於這個 ProviderNode ,可以發現這個實際上是一個 Widget

class ProviderNode extends StatefulWidget {
  /// 對應包裹起來的子組件
  final Widget child;

  /// 所有的狀態,就是在上面被收集的
  final Providers providers;

  final bool dispose;

  /// Constructor.
  const ProviderNode(
      {@required this.child, @required this.providers, this.dispose = true});

  // statefulWidget 執行 這裏的 State,並返回
  @override
  State<StatefulWidget> createState() => _ProviderNodeState(
      child: child, providers: providers, disposeProviders: dispose);
}
//
class _ProviderNodeState extends State<ProviderNode> {
  final Widget child;
  final Providers providers;

  /// Whether or not to dispose the providers when this node is removed
  /// from the tree.
  final bool disposeProviders;

  _ProviderNodeState(
      {@required this.child,
      @required this.providers,
      @required this.disposeProviders});

  @override
  Widget build(BuildContext context) {
    return _InheritedProviders(
        child: child,
        providers: providers,
        parent: _InheritedProviders.of(context));
  }

  @override
  Future<void> dispose() async {
    super.dispose();
    if (disposeProviders) {
      await providers.dispose();
    }
  }
}
/// 這裏就是 數據向下傳導的關鍵了,就好像是 React 的 Provide 組件一樣
/// 這裏的代碼還是很重要的,我就沒有進行刪除 
/// 這裏繼承了一個InheritedWidget, InheritedWidget是Flutter的一個功能型的Widget基類

/// 它能有效地將數據在當前Widget樹中向它的子widget樹傳遞

class _InheritedProviders extends InheritedWidget {
  final _InheritedProviders parent;
  final Providers providers;
  const _InheritedProviders({Widget child, this.providers, this.parent})
      : super(child: child);

  /// Finds the closest _InheritedProviders widget abocve the current widget.
  static _InheritedProviders of(BuildContext context) {
    /// inheritFromWidgetOfExactType 函數
    /// 獲取最近的給定類型的 Widget,該widget必須是InheritedWidget的子類
    /// 同時接收到 對應 數據的子組件也會在 這裏的數據變化的時候進行更新
    /// 沒錯,這段函數 就是在 flutter 內部定義的
    final widget = context.inheritFromWidgetOfExactType(_InheritedProviders);
    return widget is _InheritedProviders ? widget : null;
  }

  @override
  bool updateShouldNotify(_InheritedProviders oldWidget) {
    return parent?.updateShouldNotify(oldWidget.parent) ??
        false || providers != oldWidget.providers;
  }

  /// 下面會有 調用的時候,就是返回 相應的狀態
  /// 在 調用 Model 類進行修改,以及 獲得這裏的數據並渲染,都會用到這個
  /// 這個時候 就是 根據傳入的 泛型 來獲取 對應 的 Model
  ///  (無論是 數據 還是操作 都已經放在了這個 Model 之中)
  Provider<T> getValue<T>({ProviderScope scope}) {
    return providers.getFromType(T, scope: scope) ??
        parent?.getValue<T>(scope: scope);
  }

  /// Needed because this works at runtime for ProvideMulti.
  Provider getFromType(Type type, {ProviderScope scope}) {
    return providers.getFromType(type, scope: scope) ??
        parent?.getFromType(type, scope: scope);
  }
}

接着就是使用了,其實這裏纔是真正用到 泛型的 地方

獲取數據以及修改數據:

Provide<Counter>(
   /// 可能會有很多人和我一樣,疑惑這個 child 是幹什麼的
   builder: (context, child, counter) => Text(
       '${counter.value}',
       style: Theme.of(context).textTheme.display1,
       ),
),

========

 onPressed: () => Provide.value<Counter>(context).increment(),

源碼:

class Provide<T> extends StatelessWidget {

  final ValueBuilder<T> builder;
  final Widget child;
  final ProviderScope scope;
  const Provide({@required this.builder, this.child, this.scope});

  /// 這裏就是修改數據的 調用方法了,
  /// 靜態方法,所以需要傳入的 泛型來進行 取值,並且返回
  static T value<T>(BuildContext context, {ProviderScope scope}) {
    final provider = _InheritedProviders.of(context).getValue<T>(scope: scope);
    assert(provider != null);

    return provider.get(context);
  }

  /// 可以看到,這裏的child 實際上是 你初始化穿進去的東西原封不動的 給你了
  /// 因爲有的 widget 是很複雜且不變的,可以通過這裏 減少 widget 的重複構建
  @override
  Widget build(BuildContext context) {

     /// 正如上文所講的 ,flutter 內部自定義了 一個 上下文結構,這裏就是調用 並獲取的地方
     /// 沒有看過的可以 往上面找 _InheritedProviders 類
     /// 就好像是 Theme.of(context) 一樣的原理

    final provider = _InheritedProviders.of(context).getValue<T>(scope: scope);
    final value = provider.get(context);
    final listenable = _getListenable(provider, value);

    if (provider is Listenable) {
      return ListeningBuilder(
        listenable: listenable,
        child: child,
        builder: (buildContext, child) =>
            builder(buildContext, child, provider.get(context)),
      );
    } else if (value is Listenable) {
      return ListeningBuilder(
        listenable: listenable,
        child: child,
        builder: (buildContext, child) => builder(buildContext, child, value),
      );
    }
  }
}

以上,就是梳理了一下 我這次學習的內容了,有錯誤的也希望 同學們可以指出來,

ps:看了相關的 issue,發現這個停止更新了,慘,明明是最好用的那個

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章