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(), ), ); }
@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_ListenerEntryextendsLinkedListEntry<_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. classChangeNotifierimplementsListenable{ 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.' ); } returntrue; }()); returntrue; }
/// 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 boolget 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;
/// 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. classInheritedProvider<T> extendsSingleChildStatelessWidget{ /// 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, voidFunction(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);
/// 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;
* 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}) {} }
_InheritedProviderScope唯一特殊的地方,我们发现它自己创建了一个Element实现通过覆盖createElement函数,返回_InheritedProviderScopeElement实例,flutter三板斧Widget、Element、RenderObject,该框架自己实现一层Element,我们知道Widget是配置文件,只有build和rebuild以及remove from the tree,而Element作为一层虚拟Dom,主要负责优化,优化页面刷新的逻辑,我们详细看有一下_InheritedProviderScopeElement的源码:
@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; }
@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. '''); } returntrue; }());
@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); } returnsuper.build(); }
@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). '''), ], ); } returntrue; }()); returnsuper.dependOnInheritedElement(ancestor, aspect: aspect); }
abstractclassProxyElementextendsComponentElement{ /// 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); }
abstractclassInheritedContext<T> extendsBuildContext{ /// 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. boolget hasValue; }
/// {@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;
// 调用_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>; returnfalse; }); } else { inheritedElement = context.getElementForInheritedWidgetOfExactType< _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>; } if (inheritedElement == null) { throw ProviderNotFoundException(T, context.widget.runtimeType); } return inheritedElement; }
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; }
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.' )
@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; returntrue; }()); shouldNotify = updateShouldNotify(value); } finally { assert(() { _debugIsSelecting = false; returntrue; }()); } if (shouldNotify) { break; } } } else { shouldNotify = true; } } if (shouldNotify) { dependent.didChangeDependencies(); } }