我们先来看下ProxyProvider的官方介绍
ProxyProvider 能够将多个 provider 的值聚合为一个新对象,将结果传递给 Provider。这个新对象会在其依赖的任意一个 provider 更新后同步更新。
ProxyProvider跟Selector类似,有ProxyProvider,ProxyProvider2,ProxyProvider3,类名后的数字代表Provider的数量,对应的其他ProxyProvider还有ChangeNotifierProxyProvider、ListenableProxyProvider,同样也有ChangeNotifierProxyProvider2、ChangeNotifierProxyProvider3、ListenableProxyProvider2、ListenableProxyProvider3…等类。
一样看下Demo
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
ProxyProvider<Counter, Translations>(
update: (_, counter, __) => Translations(counter.value),
),
],
child: Builder(
builder: (context) {
return Scaffold(
body: Center(
child: Consumer<Translations>(builder: (child, model, context) {
return Text(model.title);
}),
),
floatingActionButton: FloatingActionButton(onPressed: () {
context.read<Counter>().increment();
}),
);
}
),
);
}
class Translations {
const Translations(this._value);
final int _value;
String get title => 'You clicked $_value times';
}
上面的例子中,Consumer监听Translations类型的Provider,在Counter调用notifyListeners时会通知Consumer刷新。但是Translations是一个普通类,我们一起看下内部怎么实现转换的吧。
ProxyProvider继承自ProxyProvider0,需要实现update方法,传递update方法使用Provider.of(context)建立监听,ProxyProvider0继承InheritedProvider(在《Flutter Provider状态管理源码解析》文章已经介绍过建议配合使用哦,不过那时我们关注create方法,省略了一些update代码)。ProxyProvider实际是将转换后的状态类Translations作为InhritedWidget,Consumer与其建立联系监听。那ProxyProvider怎么与依赖的状态类Counter关联呢。我们接着看。
class ProxyProvider<T, R> extends ProxyProvider0<R> {
/// Initializes [key] for subclasses.
ProxyProvider({
Key? key,
Create<R>? create,
required ProxyProviderBuilder<T, R> update,
UpdateShouldNotify<R>? updateShouldNotify,
Dispose<R>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
lazy: lazy,
builder: builder,
create: create,
update: (context, value) => update(
context,
Provider.of(context),
value,
),
updateShouldNotify: updateShouldNotify,
dispose: dispose,
child: child,
);
}
class ProxyProvider0<R> extends InheritedProvider<R> {
/// Initializes [key] for subclasses.
ProxyProvider0({
Key? key,
Create<R>? create,
required R Function(BuildContext context, R? value) update,
UpdateShouldNotify<R>? updateShouldNotify,
Dispose<R>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
lazy: lazy,
builder: builder,
create: create,
update: update,
dispose: dispose,
updateShouldNotify: updateShouldNotify,
debugCheckInvalidValueType: kReleaseMode
? null
: (R value) =>
Provider.debugCheckInvalidValueType?.call<R>(value),
child: child,
);
}
InheritedProvider会创建_delegate,_CreateInheritedProvider是_delegate的create实现,会要求create != null || update != null,在获取value时候会调用update初始化value(如果是ChangeNotifierProxyProvider会有create过程和监听),这里的startListening是无效的因为我们是用的ProxyProvider类型,没有startListening方法。那是怎么实现自动更新呢?答案就是Counter类,我们Counter是一个ChangeNotifier,还记得上面ProxyProvider用了Provider.of(context),在Counter调用notifyListeners后,会触发_InheritedProviderScopeElement的build。
class _CreateInheritedProviderState<T>
extends _DelegateState<T, _CreateInheritedProvider<T>> {
VoidCallback? _removeListener;
bool _didInitValue = false;
T? _value;
_CreateInheritedProvider<T>? _previousWidget;
FlutterErrorDetails? _initError;
@override
T get value {
if (_didInitValue && _initError != null) {
...
if (!_didInitValue) {
_didInitValue = true;
if (delegate.create != null) {
...
}
if (delegate.update != null) {
...
_value = delegate.update!(element!, _value);
...
}
}
element!._isNotifyDependentsEnabled = false;
_removeListener ??= delegate.startListening?.call(element!, _value as T);
element!._isNotifyDependentsEnabled = true;
assert(delegate.startListening == null || _removeListener != null);
return _value as T;
}
@override
void dispose() {
super.dispose();
_removeListener?.call();
if (_didInitValue) {
delegate.dispose?.call(element!, _value as T);
}
}
@override
void build({required bool isBuildFromExternalSources}) {
var shouldNotify = false;
// Don't call `update` unless the build was triggered from `updated`/`didChangeDependencies`
// otherwise `markNeedsNotifyDependents` will trigger unnecessary `update` calls
if (isBuildFromExternalSources &&
_didInitValue &&
delegate.update != null) {
...
/// 更新依赖
_value = delegate.update!(element!, _value as T);
...
if (shouldNotify) {
element!._shouldNotifyDependents = true;
}
_previousWidget = delegate;
return super.build(isBuildFromExternalSources: isBuildFromExternalSources);
}
}
_InheritedProviderScopeElement的build会触发_delegateState.build,build方法调用delegate.update!(element!, _value as T)更新数据。
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
static int _nextProviderId = 0;
bool _shouldNotifyDependents = false;
bool _debugInheritLocked = false;
bool _isNotifyDependentsEnabled = true;
bool _firstBuild = true;
bool _updatedShouldNotify = false;
bool _isBuildFromExternalSources = false;
late _DelegateState<T, _Delegate<T>> _delegateState;
late String _debugId;
...
@override
void didChangeDependencies() {
_isBuildFromExternalSources = true;
super.didChangeDependencies();
}
@override
Widget build() {
if (widget.owner._lazy == false) {
value; // this will force the value to be computed.
}
_delegateState.build(
isBuildFromExternalSources: _isBuildFromExternalSources,
);
_isBuildFromExternalSources = false;
if (_shouldNotifyDependents) {
_shouldNotifyDependents = false;
notifyClients(widget);
}
return super.build();
}
...
}
总结下,以上就是ProxyProvider实现原理啦,实际使用中ProxyProvider代理对象一般是ChangeNotifierProvider,由ChangeNotifierProvider触发更新。ChangeNotifierProxyProvider与ProxyProvider类似,ChangeNotifierProxyProvider需要实现create和update方法,可以自己触发更新。