0%

flutter探索 - Provider源码解析

上一篇中我们列举了几种状态管理,我们已经知道Provider是对InheritedWidget的包装,只是让InheritedWidget用起来更加简单且高可复用。我们上一篇也提到过InheritedWidget的缺点。

我们这篇主要带着问题,看Provider是如果规避InheritedWidget的缺点,而且Provider提供了dispose回调,你可以在该函数中主动关闭,如何做到的呢。带着这些问题,我们寻找答案。

如何使用

我们先看怎么使用provider。

1、定义一个ChangeNotifier

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Counter with ChangeNotifier, DiagnosticableTreeMixin {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
/// Makes `Counter` readable inside the devtools by listing all of its properties
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('count', count));
}
}

2、用ChangeNotifierProvider来订阅Counter

不难猜出,ChangeNotifierProvider肯定是InheritedWidget的包装类,负责将Counter的状态共享给子Widget。

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
runApp(
/// Providers are above [MyApp] instead of inside it, so that tests
/// can use [MyApp] while mocking the providers
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
],
child: const MyApp(),
),
);
}

3、接收数据通过Consumer<Counter>

Consumer是个消费者,它负责消费ChangeNotifierProvider生产的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('You have pushed the button this many times:'),

/// Extracted as a separate widget for performance optimization.
/// As a separate widget, it will rebuild independently from [MyHomePage].
///
/// This is totally optional (and rarely needed).
/// Similarly, we could also use [Consumer] or [Selector].
Consume<Counter>(
builder:(BuildContext context,Counter value,Widget child){
return Text('${value.count}')
},
),
Count(),
],
),
),
floatingActionButton: FloatingActionButton(
key: const Key('increment_floatingActionButton'),

/// Calls `context.read` instead of `context.watch` so that it does not rebuild
/// when [Counter] changes.
onPressed: () => context.read<Counter>().increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

class Count extends StatelessWidget {
const Count({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Text(

/// Calls `context.watch` to make [Count] rebuild when [Counter] changes.
'${context.watch<Counter>().count}',
key: const Key('counterState'),
style: Theme.of(context).textTheme.headline4);
}
}

通过看Provider的用法例子,可以判断出Provider的封装足够易用,而且Counter作为Model层使用with ChangeNotifier而不是继承,所以说它的侵入性也比较低。下面我们看InheritedWidget的缺点看是否已经规避

1、 容易造成不必要的刷新(看是否已经解决)

我们家两个子Widget进去,排在Consumer的后面,OtherWidget什么都不干,不去订阅Counter,OtherWidget2通过contex.watch<Counter>().count函数监听而不是Consumer,来看一下效果一样不,然后在build函数中都加入print

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class OtherWidget extends StatelessWidget{
@override
Widget build(BuildContext context) {
print('OtherWidget build');
//
return Text('OtherWidget');
}
}
class OtherWidget2 extends StatelessWidget{
@override
Widget build(BuildContext context) {
print('OtherWidget2 build');
//
return Text('OtherWidget2 ${context.watch<Counter>().count}');
}
}

可以看出几个结论

  • Consumer、context.watch都可以监听Counter变化
  • Consumer只会刷新自己
  • context.watch所在的子widget不管是否是const都会被重建后刷新数据
  • OtherWidget并没有被重建,因为它没有订阅Counter

局部刷新确实实现了通过Consumer,第二个问题不支持跨页面router的状态,这个不支持,第三个问题数据是不可变的(只读),经过这个例子可以分辨出数据确实是可变的,如何变化的我们分析源码找本质。

ChangeNotifier和ChangeNotifierProvider是被观察者和观察者的关系。ChangeNotifierProvider和Cosumer是生产者和消费者的关系。

源码分析

ChangeNotifier

在包package:meta/meta.dart下,是flutter sdk的代码,并不属于Provider框架的一部分。通过查看代码可以看出,这是一个标准的观察者模型,而真正的监听者就是typedef VoidCallback = void Function();是dart.ui包下定义的一个函数,没有任何返回参数的函数。ChangeNotifer实现抽象类Listenable,通过源码注释我们看到Listenable是一个转么负责维护监听列表的一个抽象类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
class _ListenerEntry extends LinkedListEntry<_ListenerEntry> {
_ListenerEntry(this.listener);
final VoidCallback listener;//真正的监听者
}
/// A class that can be extended or mixed in that provides a change notification
/// API using [VoidCallback] for notifications.
///
/// It is O(1) for adding listeners and O(N) for removing listeners and dispatching
/// notifications (where N is the number of listeners).
///
/// See also:
///
/// * [ValueNotifier], which is a [ChangeNotifier] that wraps a single value.
class ChangeNotifier implements Listenable {
LinkedList<_ListenerEntry>? _listeners = LinkedList<_ListenerEntry>();

bool _debugAssertNotDisposed() {
assert(() {
if (_listeners == null) {
throw FlutterError(
'A $runtimeType was used after being disposed.\n'
'Once you have called dispose() on a $runtimeType, it can no longer be used.'
);
}
return true;
}());
return true;
}

/// Whether any listeners are currently registered.
///
/// Clients should not depend on this value for their behavior, because having
/// one listener's logic change when another listener happens to start or stop
/// listening will lead to extremely hard-to-track bugs. Subclasses might use
/// this information to determine whether to do any work when there are no
/// listeners, however; for example, resuming a [Stream] when a listener is
/// added and pausing it when a listener is removed.
///
/// Typically this is used by overriding [addListener], checking if
/// [hasListeners] is false before calling `super.addListener()`, and if so,
/// starting whatever work is needed to determine when to call
/// [notifyListeners]; and similarly, by overriding [removeListener], checking
/// if [hasListeners] is false after calling `super.removeListener()`, and if
/// so, stopping that same work.
@protected
bool get hasListeners {
assert(_debugAssertNotDisposed());
return _listeners!.isNotEmpty;
}

/// Register a closure to be called when the object changes.
///
/// This method must not be called after [dispose] has been called.
@override
void addListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
_listeners!.add(_ListenerEntry(listener));
}

/// Remove a previously registered closure from the list of closures that are
/// notified when the object changes.
///
/// If the given listener is not registered, the call is ignored.
///
/// This method must not be called after [dispose] has been called.
///
/// If a listener had been added twice, and is removed once during an
/// iteration (i.e. in response to a notification), it will still be called
/// again. If, on the other hand, it is removed as many times as it was
/// registered, then it will no longer be called. This odd behavior is the
/// result of the [ChangeNotifier] not being able to determine which listener
/// is being removed, since they are identical, and therefore conservatively
/// still calling all the listeners when it knows that any are still
/// registered.
///
/// This surprising behavior can be unexpectedly observed when registering a
/// listener on two separate objects which are both forwarding all
/// registrations to a common upstream object.
@override
void removeListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
for (final _ListenerEntry entry in _listeners!) {
if (entry.listener == listener) {
entry.unlink();
return;
}
}
}

/// Discards any resources used by the object. After this is called, the
/// object is not in a usable state and should be discarded (calls to
/// [addListener] and [removeListener] will throw after the object is
/// disposed).
///
/// This method should only be called by the object's owner.
@mustCallSuper
void dispose() {
assert(_debugAssertNotDisposed());
_listeners = null;
}

/// Call all the registered listeners.
///
/// Call this method whenever the object changes, to notify any clients the
/// object may have changed. Listeners that are added during this iteration
/// will not be visited. Listeners that are removed during this iteration will
/// not be visited after they are removed.
///
/// Exceptions thrown by listeners will be caught and reported using
/// [FlutterError.reportError].
///
/// This method must not be called after [dispose] has been called.
///
/// Surprising behavior can result when reentrantly removing a listener (i.e.
/// in response to a notification) that has been registered multiple times.
/// See the discussion at [removeListener].
@protected
@visibleForTesting
void notifyListeners() {
assert(_debugAssertNotDisposed());
if (_listeners!.isEmpty)
return;

final List<_ListenerEntry> localListeners = List<_ListenerEntry>.from(_listeners!);

for (final _ListenerEntry entry in localListeners) {
try {
if (entry.list != null)
entry.listener();
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'foundation library',
context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () sync* {
yield DiagnosticsProperty<ChangeNotifier>(
'The $runtimeType sending notification was',
this,
style: DiagnosticsTreeStyle.errorProperty,
);
},
));
}
}
}
}

ChangeNotifierProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class ChangeNotifierProvider<T extends ChangeNotifier>
extends ListenableProvider<T> {
/// Creates a [ChangeNotifier] using `create` and automatically
/// dispose it when [ChangeNotifierProvider] is removed from the widget tree.
///
/// `create` must not be `null`.
ChangeNotifierProvider({
Key key,
@required Create<T> create,
bool lazy,
TransitionBuilder builder,
Widget child,
}) : super(
key: key,
create: create,
dispose: _dispose,
lazy: lazy,
builder: builder,
child: child,
);
/// Provides an existing [ChangeNotifier].
ChangeNotifierProvider.value({
Key key,
@required T value,
TransitionBuilder builder,
Widget child,
}) : super.value(
key: key,
builder: builder,
value: value,
child: child,
);

static void _dispose(BuildContext context, ChangeNotifier notifier) {
notifier?.dispose();
}
}

分析一下构造

  • Create<T> create

    是个通用函数typedef Create<T> = T Function(BuildContext context)用于创建T类,这里负责创建ChangeNotifier

  • bool lazy

    是否是懒加载

  • TransitionBuilder builder

    当builder存在时将不会用child作为子widget,追溯源码实现,根据继承关系找到InheritedProvider:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @override
    Widget buildWithChild(BuildContext context, Widget child) {
    assert(
    builder != null || child != null,
    '$runtimeType used outside of MultiProvider must specify a child',
    );
    return _InheritedProviderScope<T>(
    owner: this,
    child: builder != null
    ? Builder(
    builder: (context) => builder(context, child),
    )
    : child,
    );
    }
  • Widget child

    builder不存在时就用child。

ChangeNotifierProvider继承 ListenableProvider,我们我们继续看ListenableProvider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class ListenableProvider<T extends Listenable> extends InheritedProvider<T> {
/// Creates a [Listenable] using [create] and subscribes to it.
///
/// [dispose] can optionally passed to free resources
/// when [ListenableProvider] is removed from the tree.
///
/// [create] must not be `null`.
ListenableProvider({
Key key,
@required Create<T> create,
Dispose<T> dispose,
bool lazy,
TransitionBuilder builder,
Widget child,
}) : assert(create != null),
super(
key: key,
startListening: _startListening,
create: create,
dispose: dispose,
lazy: lazy,
builder: builder,
child: child,
);

/// Provides an existing [Listenable].
ListenableProvider.value({
Key key,
@required T value,
UpdateShouldNotify<T> updateShouldNotify,
TransitionBuilder builder,
Widget child,
}) : super.value(
key: key,
builder: builder,
value: value,
updateShouldNotify: updateShouldNotify,
startListening: _startListening,
child: child,
);

static VoidCallback _startListening(
InheritedContext<Listenable> e,
Listenable value,
) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
}
  • Listenable上面已经分析,它是负责管理观察者列表的抽象
  • 它比子类ChangeNotifierProvider多了一个构造函数dispose,这个函数是 typedef Dispose<T> = void Function(BuildContext context, T value);是个回调,应该是当页面被销毁时出发(需要继续探究源码,目前只是猜测)。

因为ListenableProvider是继承自InheritedProvider,我们继续看InheritedProvider的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/// A generic implementation of an [InheritedWidget].
///
/// Any descendant of this widget can obtain `value` using [Provider.of].
///
/// Do not use this class directly unless you are creating a custom "Provider".
/// Instead use [Provider] class, which wraps [InheritedProvider].
///
/// See also:
///
/// - [DeferredInheritedProvider], a variant of this object where the provided
/// object and the created object are two different entity.
class InheritedProvider<T> extends SingleChildStatelessWidget {
/// Creates a value, then expose it to its descendants.
///
/// The value will be disposed of when [InheritedProvider] is removed from
/// the widget tree.
InheritedProvider({
Key key,
Create<T> create,
T Function(BuildContext context, T value) update,
UpdateShouldNotify<T> updateShouldNotify,
void Function(T value) debugCheckInvalidValueType,
StartListening<T> startListening,
Dispose<T> dispose,
this.builder,
bool lazy,
Widget child,
}) : _lazy = lazy,
_delegate = _CreateInheritedProvider(
create: create,
update: update,
updateShouldNotify: updateShouldNotify,
debugCheckInvalidValueType: debugCheckInvalidValueType,
startListening: startListening,
dispose: dispose,
),
super(key: key, child: child);

/// Expose to its descendants an existing value,
InheritedProvider.value({
Key key,
@required T value,
UpdateShouldNotify<T> updateShouldNotify,
StartListening<T> startListening,
bool lazy,
this.builder,
Widget child,
}) : _lazy = lazy,
_delegate = _ValueInheritedProvider(
value: value,
updateShouldNotify: updateShouldNotify,
startListening: startListening,
),
super(key: key, child: child);

InheritedProvider._constructor({
Key key,
_Delegate<T> delegate,
bool lazy,
this.builder,
Widget child,
}) : _lazy = lazy,
_delegate = delegate,
super(key: key, child: child);

final _Delegate<T> _delegate;
final bool _lazy;

/// Syntax sugar for obtaining a [BuildContext] that can read the provider
/// created.
///
/// This code:
///
/// ```dart
/// Provider<int>(
/// create: (context) => 42,
/// builder: (context, child) {
/// final value = context.watch<int>();
/// return Text('$value');
/// }
/// )
///

///
/// is strictly equivalent to:
///
/// dart /// Provider<int>( /// create: (context) => 42, /// child: Builder( /// builder: (context) { /// final value = context.watch<int>(); /// return Text('$value'); /// }, /// ), /// ) ///
///
/// For an explanation on the child parameter that builder receives,
/// see the “Performance optimizations” section of [AnimatedBuilder].
final TransitionBuilder builder;

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
_delegate.debugFillProperties(properties);
}

@override
_InheritedProviderElement createElement() {
return _InheritedProviderElement(this);
}

@override
Widget buildWithChild(BuildContext context, Widget child) {
assert(
builder != null || child != null,
‘$runtimeType used outside of MultiProvider must specify a child’,
);
return _InheritedProviderScope(
owner: this,
child: builder != null
? Builder(
builder: (context) => builder(context, child),
)
: child,
);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

构造函数中多出来的参数:

* T Function(BuildContext context, T value) update,该函数返回数据变更值value,具体实现在\_CreateInheritedProvider类中,说白了InheritedProvider\<T>是无状态的,他要变更的话依赖于\_CreateInheritedProvider类,\_CreateInheritedProvider是\_Delegate的实现类,\_Delegate是一个状态代理类,来看一下\_Delegate的实现

```dart
@immutable
abstract class _Delegate<T> {
_DelegateState<T, _Delegate<T>> createState();

void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
}

abstract class _DelegateState<T, D extends _Delegate<T>> {
_InheritedProviderScopeElement<T> element;

T get value;

D get delegate => element.widget.owner._delegate as D;

bool get hasValue;

bool debugSetInheritedLock(bool value) {
return element._debugSetInheritedLock(value);
}

bool willUpdateDelegate(D newDelegate) => false;

void dispose() {}

void debugFillProperties(DiagnosticPropertiesBuilder properties) {}

void build({@required bool isBuildFromExternalSources}) {}
}

这里用到了委托模式,这里就有点类似StatefulWidget和State的关系,同样的_DelegateState提供了类似生命周期的函数,如willUpdateDelegate更新新的委托,dispose注销等。

  • UpdateShouldNotify<T> updateShouldNotify,
    void Function(T value) debugCheckInvalidValueType,
    StartListening<T> startListening,
    Dispose<T> dispose,这些函数全部交给了委托类。
  • 最关键的实现来了,到目前为止还没看到InheritedWidget的逻辑,他来了Widget buildWithChild(BuildContext context, Widget child),我们传入的Widget就被叫_InheritedProviderScope的类包裹了,看一下源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class _InheritedProviderScope<T> extends InheritedWidget {
const _InheritedProviderScope({
this.owner,
@required Widget child,
}) : super(child: child);
final InheritedProvider<T> owner;
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return false;
}
@override
_InheritedProviderScopeElement<T> createElement() {
return _InheritedProviderScopeElement<T>(this);
}
}

至此你有没有发现一个特点,所有的函数都被_Delegate带走了,剩下的只有Widget交给了_InheritedProviderScope,这个设计很好,毕竟InheritedWidget其实也就只能做到数据共享,跟函数并没有关系,唯一有关系的地方,猜测在InheritedWidget提供的Widget中调用。

一个细节:this在buildWithChild函数中,将InheritedProvider本身传递给InheritedWidget,应该是为了方便调用它的_Delegate委托类,用来回调各种函数。

_InheritedProviderScope唯一特殊的地方,我们发现它自己创建了一个Element实现通过覆盖createElement函数,返回_InheritedProviderScopeElement实例,flutter三板斧Widget、Element、RenderObject,该框架自己实现一层Element,我们知道Widget是配置文件,只有build和rebuild以及remove from the tree,而Element作为一层虚拟Dom,主要负责优化,优化页面刷新的逻辑,我们详细看有一下_InheritedProviderScopeElement的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//继承自InheritedElement,因为InheritedWidget对应的Element就是它
//实现InheritedContext,InheritedContext继承自BuildContext,多了个T泛型
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
//构造函数,将Element对应的Widget传进来
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
//是否需要通知依赖的Element变更
bool _shouldNotifyDependents = false;
bool _debugInheritLocked = false;
//是否允许通知变更
bool _isNotifyDependentsEnabled = true;
//是否第一次构建
bool _firstBuild = true;
//是否更新newWidget的Delegate委托
bool _updatedShouldNotify = false;
//这个变量就是控制的数据变更,在Widget变更和Element依赖变更的时候都会被设置为true
bool _isBuildFromExternalSources = false;
//委托类的状态 this就是为了那到上层的委托类
_DelegateState<T, _Delegate<T>> _delegateState;
@override
_InheritedProviderScope<T> get widget =>
super.widget as _InheritedProviderScope<T>;
@override
void reassemble() {
super.reassemble();
final value = _delegateState.hasValue ? _delegateState.value : null;
if (value is ReassembleHandler) {
value.reassemble();
}
}
@override
void updateDependencies(Element dependent, Object aspect) {
final dependencies = getDependencies(dependent);
// once subscribed to everything once, it always stays subscribed to everything
if (dependencies != null && dependencies is! _Dependency<T>) {
return;
}
if (aspect is _SelectorAspect<T>) {
final selectorDependency =
(dependencies ?? _Dependency<T>()) as _Dependency<T>;

if (selectorDependency.shouldClearSelectors) {
selectorDependency.shouldClearSelectors = false;
selectorDependency.selectors.clear();
}
if (selectorDependency.shouldClearMutationScheduled == false) {
selectorDependency.shouldClearMutationScheduled = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
selectorDependency
..shouldClearMutationScheduled = false
..shouldClearSelectors = true;
});
}
selectorDependency.selectors.add(aspect);
setDependencies(dependent, selectorDependency);
} else {
// subscribes to everything
setDependencies(dependent, const Object());
}
}

@override
void notifyDependent(InheritedWidget oldWidget, Element dependent) {
final dependencies = getDependencies(dependent);

var shouldNotify = false;
if (dependencies != null) {
if (dependencies is _Dependency<T>) {
// select can never be used inside `didChangeDependencies`, so if the
// dependent is already marked as needed build, there is no point
// in executing the selectors.
if (dependent.dirty) {
return;
}

for (final updateShouldNotify in dependencies.selectors) {
try {
assert(() {
_debugIsSelecting = true;
return true;
}());
shouldNotify = updateShouldNotify(value);
} finally {
assert(() {
_debugIsSelecting = false;
return true;
}());
}
if (shouldNotify) {
break;
}
}
} else {
shouldNotify = true;
}
}

if (shouldNotify) {
dependent.didChangeDependencies();
}
}

@override
void performRebuild() {
if (_firstBuild) {
_firstBuild = false;
_delegateState = widget.owner._delegate.createState()..element = this;
}
super.performRebuild();
}

@override
void update(_InheritedProviderScope<T> newWidget) {
assert(() {
if (widget.owner._delegate.runtimeType !=
newWidget.owner._delegate.runtimeType) {
throw StateError('''
Rebuilt $widget using a different constructor.

This is likely a mistake and is unsupported.
If you're in this situation, consider passing a `key` unique to each individual constructor.
''');
}
return true;
}());

_isBuildFromExternalSources = true;
_updatedShouldNotify =
_delegateState.willUpdateDelegate(newWidget.owner._delegate);
super.update(newWidget);
_updatedShouldNotify = false;
}

@override
void updated(InheritedWidget oldWidget) {
super.updated(oldWidget);
if (_updatedShouldNotify) {
notifyClients(oldWidget);
}
}

@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();
}

@override
void unmount() {
_delegateState.dispose();
super.unmount();
}

@override
bool get hasValue => _delegateState.hasValue;

@override
void markNeedsNotifyDependents() {
if (!_isNotifyDependentsEnabled) {
return;
}

markNeedsBuild();
_shouldNotifyDependents = true;
}

bool _debugSetInheritedLock(bool value) {
assert(() {
_debugInheritLocked = value;
return true;
}());
return true;
}

@override
T get value => _delegateState.value;

@override
InheritedWidget dependOnInheritedElement(
InheritedElement ancestor, {
Object aspect,
}) {
assert(() {
if (_debugInheritLocked) {
throw FlutterError.fromParts(
<DiagnosticsNode>[
ErrorSummary(
'Tried to listen to an InheritedWidget '
'in a life-cycle that will never be called again.',
),
ErrorDescription('''
This error typically happens when calling Provider.of with `listen` to `true`,
in a situation where listening to the provider doesn't make sense, such as:
- initState of a StatefulWidget
- the "create" callback of a provider

This is undesired because these life-cycles are called only once in the
lifetime of a widget. As such, while `listen` is `true`, the widget has
no mean to handle the update scenario.

To fix, consider:
- passing `listen: false` to `Provider.of`
- use a life-cycle that handles updates (like didChangeDependencies)
- use a provider that handles updates (like ProxyProvider).
'''),
],
);
}
return true;
}());
return super.dependOnInheritedElement(ancestor, aspect: aspect);
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
_delegateState.debugFillProperties(properties);
}
}
  • void update(_InheritedProviderScope<T> newWidget)让页面重新build是在这里,因为InheritedElement继承自ProxyElement,而ProxyElement的update函数调用了两个函数updated(已更新完成),rebuild函数出发重新build逻辑,下面是代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    abstract class ProxyElement extends ComponentElement {
    /// Initializes fields for subclasses.
    ProxyElement(ProxyWidget widget) : super(widget);
    @override
    ProxyWidget get widget => super.widget as ProxyWidget;
    @override
    Widget build() => widget.child;
    @override
    void update(ProxyWidget newWidget) {
    final ProxyWidget oldWidget = widget;
    assert(widget != null);
    assert(widget != newWidget);
    super.update(newWidget);
    assert(widget == newWidget);
    updated(oldWidget);
    _dirty = true;
    rebuild();
    }
    /// Called during build when the [widget] has changed.
    ///
    /// By default, calls [notifyClients]. Subclasses may override this method to
    /// avoid calling [notifyClients] unnecessarily (e.g. if the old and new
    /// widgets are equivalent).
    @protected
    void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
    }
  • performRebuild()是在update触发真正调用rebuild之后被调用。

  • updateDependencies、notifyDependent处理Element依赖逻辑

  • update、updated处理widget更新逻辑

  • didChangeDependencies当此State对象的依赖项更改时调用,子类很少重写此方法,因为框架总是在依赖项更改后调用build。一些子类确实重写了此方法,因为当他们的依存关系发生变化时,他们需要做一些昂贵的工作(如:网络获取),并且对于每个构建而言这些工作将太昂贵。

  • build()构建需要的widget,Element在调用build的时候也会触发Widget的build

  • void unmount()这里看到了_delegateState.dispose();的调用,当Element从树上移除的时候,回调了dispose函数

来看一个生命周期的图:

widget_lifecycle

  • notifyClients是InheritedElement中实现的函数,通过官方文档了解到,它是通过调用Element.didChangeDependencies通知所有从属Element此继承的widget已更改,此方法只能在构建阶段调用,通常,在重建Inherited widget时自动调用此方法,还有InheritedNotifier,它是InheritedWidget的子类,在其Listenable发送通知时也调用此方法。

  • markNeedsNotifyDependents,如果调用它,会强制build后,通知所有依赖Element刷新widget,看下面代码,发现该函数在InheritedContext中定义,所以我们可以通过InheritedContext上下文来强制页面的构建。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    abstract class InheritedContext<T> extends BuildContext {
    /// The current value exposed by [InheritedProvider].
    ///
    /// This property is lazy loaded, and reading it the first time may trigger
    /// some side-effects such as creating a [T] instance or start a subscription.
    T get value;

    /// Marks the [InheritedProvider] as needing to update dependents.
    ///
    /// This bypass [InheritedWidget.updateShouldNotify] and will force widgets
    /// that depends on [T] to rebuild.
    void markNeedsNotifyDependents();

    /// Wether `setState` was called at least once or not.
    ///
    /// It can be used by [DeferredStartListening] to differentiate between the
    /// very first listening, and a rebuild after `controller` changed.
    bool get hasValue;
    }

小结

我们先回顾一下我们是如何使用InheritedWidget的,为了能让InheritedWidget的子Widget能够刷新,我们不得不依赖于Statefulwidget,并通过State控制刷新Element,调用setState刷新页面,其实是底层调用_element.markNeedsBuild()函数,这样我们明白了,其实最终控制页面的还是Element,那么Provider它也巧妙的封装了自己的_delegateState,是私有的,并没有给我们公开是用,也没有提供类似setState,单可以通过markNeedsNotifyDependents大号和setState一样的调用效果,一样的都是让所有子Widget进行重建,可我们要局部刷新呢,那就是Consumer。

Consumer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Consumer<T> extends SingleChildStatelessWidget {
/// {@template provider.consumer.constructor}
/// Consumes a [Provider<T>]
/// {@endtemplate}
Consumer({
Key key,
@required this.builder,
Widget child,
}) : assert(builder != null),
super(key: key, child: child);

/// {@template provider.consumer.builder}
/// Build a widget tree based on the value from a [Provider<T>].
///
/// Must not be `null`.
/// {@endtemplate}
final Widget Function(BuildContext context, T value, Widget child) builder;

@override
Widget buildWithChild(BuildContext context, Widget child) {
return builder(
context,
Provider.of<T>(context),
child,
);
}
}
  • 这里源码有点绕,widget child传给了父类 SingleChildStatelessWidget,最终通过buildWithChild函数的参数child传递回来,而Builder函数收到了此child,然后再组合child和需要刷新的widget组合一个新的widget给Consumer。一句话就是Consumer的构造函数可以传两个widget,一个是buider,一个是child,最终是通过builder构建最终的widget,如果child不为空,那么你需要自己组织child和builder中返回widget的关系。

  • Provider.of<T>(context)获取共享数据,怎么获取的,继续看代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    // 调用_inheritedElementOf函数
    static T of<T>(BuildContext context, {bool listen = true}) {
    assert(context != null);
    assert(
    context.owner.debugBuilding ||
    listen == false ||
    debugIsInInheritedProviderUpdate,
    '''
    Tried to listen to a value exposed with provider, from outside of the widget tree.

    This is likely caused by an event handler (like a button's onPressed) that called
    Provider.of without passing `listen: false`.

    To fix, write:
    Provider.of<$T>(context, listen: false);

    It is unsupported because may pointlessly rebuild the widget associated to the
    event handler, when the widget tree doesn't care about the value.

    The context used was: $context
    ''',
    );

    final inheritedElement = _inheritedElementOf<T>(context);

    if (listen) {
    context.dependOnInheritedElement(inheritedElement);
    }

    return inheritedElement.value;
    }

    static _InheritedProviderScopeElement<T> _inheritedElementOf<T>(
    BuildContext context,
    ) {
    assert(context != null, '''
    Tried to call context.read/watch/select or similar on a `context` that is null.

    This can happen if you used the context of a StatefulWidget and that
    StatefulWidget was disposed.
    ''');
    assert(
    _debugIsSelecting == false,
    'Cannot call context.read/watch/select inside the callback of a context.select',
    );
    assert(
    T != dynamic,
    '''
    Tried to call Provider.of<dynamic>. This is likely a mistake and is therefore
    unsupported.

    If you want to expose a variable that can be anything, consider changing
    `dynamic` to `Object` instead.
    ''',
    );
    _InheritedProviderScopeElement<T> inheritedElement;

    if (context.widget is _InheritedProviderScope<T>) {
    // An InheritedProvider<T>'s update tries to obtain a parent provider of
    // the same type.
    context.visitAncestorElements((parent) {
    inheritedElement = parent.getElementForInheritedWidgetOfExactType<
    _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>;
    return false;
    });
    } else {
    inheritedElement = context.getElementForInheritedWidgetOfExactType<
    _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>;
    }
    if (inheritedElement == null) {
    throw ProviderNotFoundException(T, context.widget.runtimeType);
    }
    return inheritedElement;
    }
  • 通过visitAncestorElements往复机查找_InheritedProviderScope的实现类也就是InheritedWidget,当找到时就返回_InheritedProviderScopeElement,而_InheritedProviderScopeElement正好可以拿到value,这个value也就是_delegateState的value

走到这里只是实现了读取数据,那么数据到底是如何刷新的呢?我们回过头来看下面极端代码:

1、Model数据调用ChangeNotifier提供的函数notifyListeners

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@protected
@visibleForTesting
void notifyListeners() {
assert(_debugAssertNotDisposed());
if (_listeners!.isEmpty)
return;
final List<_ListenerEntry> localListeners = List<_ListenerEntry>.from(_listeners!);
for (final _ListenerEntry entry in localListeners) {
try {
if (entry.list != null)
entry.listener();
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'foundation library',
context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () sync* {
yield DiagnosticsProperty<ChangeNotifier>(
'The $runtimeType sending notification was',
this,
style: DiagnosticsTreeStyle.errorProperty,
);
},
));
}
}
}

这个时候遍历所有的监听,然后执行函数listener(),这里其实等于执行VoidCallback的实例,那这个listener到底是哪个函数?

2、在ChangeNotifierProvider父类ListenableProvider的静态函数中,自动订阅了观察者,前面说了观察者就是一个普通函数,而e.markNeedsNotifyDependents就是InheritedContext的一个函数,当你notifyListemers的时候执行的就是它markNeedsNotifyDependents,上面我们知道了markNeedsNotifyDependents类似setState效果,就这样实现了UI的刷新。

1
2
3
4
5
6
7
8
//ListenableProvider 的静态函数
static VoidCallback _startListening(
InheritedContext<Listenable> e,
Listenable value,
) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}

现在还有一个点就是局部刷新,我们继续寻找

1
2
3
4
5
6
7
8
@override
Widget buildWithChild(BuildContext context, Widget child) {
return builder(
context,
Provider.of<T>(context),
child,
);
}

Consumer通过Provider.of<T>(context)这段代码才能监听到数据,而且刷新的内容也只是这一部分,我们再看下它的实现发现另一个细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  static T of<T>(BuildContext context, {bool listen = true}) {
assert(context != null);
assert(
context.owner.debugBuilding ||
listen == false ||
debugIsInInheritedProviderUpdate,
'''
Tried to listen to a value exposed with provider, from outside of the widget tree.

This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.

To fix, write:
Provider.of<$T>(context, listen: false);

It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.

The context used was: $context
''',
);

final inheritedElement = _inheritedElementOf<T>(context);

if (listen) {
context.dependOnInheritedElement(inheritedElement);
}
return inheritedElement.value;
}

它调用了BuildContext的dependOnInheritedElement函数,这个函数做了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
/// Obtains the nearest widget of the given type, which must be the type of a
/// concrete [InheritedWidget] subclass, and registers this build context with
/// that widget such that when that widget changes (or a new widget of that
/// type is introduced, or the widget goes away), this build context is
/// rebuilt so that it can obtain new values from that widget.
///
/// This method is deprecated. Please use [dependOnInheritedWidgetOfExactType] instead.
// TODO(a14n): Remove this when it goes to stable, https://github.com/flutter/flutter/pull/44189
@Deprecated(
'Use dependOnInheritedWidgetOfExactType instead. '
'This feature was deprecated after v1.12.1.'
)

触发updateDependencies,通过setDependencies,将Element缓存到_dependetns Map中,最后通过下面代码更新:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@override
void notifyDependent(InheritedWidget oldWidget, Element dependent) {
final dependencies = getDependencies(dependent);
var shouldNotify = false;
if (dependencies != null) {
if (dependencies is _Dependency<T>) {
// select can never be used inside `didChangeDependencies`, so if the
// dependent is already marked as needed build, there is no point
// in executing the selectors.
if (dependent.dirty) {
return;
}
for (final updateShouldNotify in dependencies.selectors) {
try {
assert(() {
_debugIsSelecting = true;
return true;
}());
shouldNotify = updateShouldNotify(value);
} finally {
assert(() {
_debugIsSelecting = false;
return true;
}());
}
if (shouldNotify) {
break;
}
}
} else {
shouldNotify = true;
}
}
if (shouldNotify) {
dependent.didChangeDependencies();
}
}

所以说整体流程是这样,当notifyListeners的时候其实是出发了InheritedWidget的performRebuild,再到build,build后触发notifyClients,notifyClients触发notifyDependent,notifyDependent这个时候通过getDependencies获取缓存好的Element,最终确定是否需要刷新然后调用dependent.didChangeDependencies()更新,只要widget中通过Provider.of函数订阅后,就会被InheritedWidget缓存在一个Map中,然后刷新页面的时候,如果子Widget不在缓存的Map中,就不会走刷新,而且如果shouldNotify变量是false也不会刷新,这个控制可能是虽然widget订阅了,但它自己就是不刷新,可以更加细粒度的控制。

总结

  • Provider通过缓存inheritedElement实现局部刷新。
  • 通过控制自己实现的Element层来更新UI
  • 通过Element提供的unmount函数回调dispose,实现选择性释放。

provider_class_view

转载自