作爲一名前端,關於 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,發現這個停止更新了,慘,明明是最好用的那個