part of 'provider.dart'; /// A callback used to handle the subscription of `controller`. /// /// It is expected to start the listening process and return a callback /// that will later be used to stop that listening. /// /// See also: /// /// - [DeferredInheritedProvider] /// - [StartListening], a simpler version of this typedef. typedef DeferredStartListening = VoidCallback Function( InheritedContext context, void Function(R value) setState, T controller, R? value, ); /// An [InheritedProvider] where the object listened is _not_ the object /// emitted. /// /// For example, for a stream provider, we'll want to listen to `Stream`, /// but expose `T` not the [Stream]. /// /// See also: /// /// - [InheritedProvider], a variant of this object where the provider object and /// the created object are the same. class DeferredInheritedProvider extends InheritedProvider { /// Lazily create an object automatically disposed when /// [DeferredInheritedProvider] is removed from the tree. /// /// The object create will be listened using `startListening`, and its content /// will be exposed to `child` and its descendants. DeferredInheritedProvider({ Key? key, required Create create, Dispose? dispose, required DeferredStartListening startListening, UpdateShouldNotify? updateShouldNotify, bool? lazy, TransitionBuilder? builder, Widget? child, }) : super._constructor( key: key, child: child, lazy: lazy, builder: builder, delegate: _CreateDeferredInheritedProvider( create: create, dispose: dispose, updateShouldNotify: updateShouldNotify, startListening: startListening, ), ); /// Listens to `value` and expose its content to `child` and its descendants. DeferredInheritedProvider.value({ Key? key, required T value, required DeferredStartListening startListening, UpdateShouldNotify? updateShouldNotify, bool? lazy, TransitionBuilder? builder, Widget? child, }) : super._constructor( key: key, lazy: lazy, builder: builder, delegate: _ValueDeferredInheritedProvider( value, updateShouldNotify, startListening, ), child: child, ); } abstract class _DeferredDelegate extends _Delegate { _DeferredDelegate(this.updateShouldNotify, this.startListening); final UpdateShouldNotify? updateShouldNotify; final DeferredStartListening startListening; @override _DeferredDelegateState> createState(); } abstract class _DeferredDelegateState> extends _DelegateState { VoidCallback? _removeListener; T get controller; R? _value; @override R get value { // setState should be no-op inside startListening, as it's lazy-loaded // otherwise Flutter will throw an exception for no reason. element!._isNotifyDependentsEnabled = false; _removeListener ??= delegate.startListening( element!, setState, controller, _value, ); element!._isNotifyDependentsEnabled = true; assert(element!.hasValue, ''' The callback "startListening" was called, but it left DeferredInhertitedProviderElement<$T, $R> in an uninitialized state. It is necessary for "startListening" to call "setState" at least once the very first time "value" is requested. To fix, consider: DeferredInheritedProvider( ..., startListening: (element, setState, controller, value) { if (!element.hasValue) { setState(myInitialValue); // TODO replace myInitialValue with your own } ... } ) '''); assert(_removeListener != null); return _value as R; } @override void dispose() { super.dispose(); _removeListener?.call(); } bool get isLoaded => _removeListener != null; bool _hasValue = false; @override bool get hasValue => _hasValue; void setState(R value) { if (_hasValue) { final shouldNotify = delegate.updateShouldNotify != null ? delegate.updateShouldNotify!(_value as R, value) : _value != value; if (shouldNotify) { element!.markNeedsNotifyDependents(); } } _hasValue = true; _value = value; } } class _CreateDeferredInheritedProvider extends _DeferredDelegate { _CreateDeferredInheritedProvider({ required this.create, this.dispose, UpdateShouldNotify? updateShouldNotify, required DeferredStartListening startListening, }) : super(updateShouldNotify, startListening); final Create create; final Dispose? dispose; @override _CreateDeferredInheritedProviderElement createState() { return _CreateDeferredInheritedProviderElement(); } } class _CreateDeferredInheritedProviderElement extends _DeferredDelegateState> { bool _didBuild = false; T? _controller; @override T get controller { if (!_didBuild) { assert(debugSetInheritedLock(true)); bool? _debugPreviousIsInInheritedProviderCreate; bool? _debugPreviousIsInInheritedProviderUpdate; assert(() { _debugPreviousIsInInheritedProviderCreate = debugIsInInheritedProviderCreate; _debugPreviousIsInInheritedProviderUpdate = debugIsInInheritedProviderUpdate; return true; }()); try { assert(() { debugIsInInheritedProviderCreate = true; debugIsInInheritedProviderUpdate = false; return true; }()); _controller = delegate.create(element!); } finally { assert(() { debugIsInInheritedProviderCreate = _debugPreviousIsInInheritedProviderCreate!; debugIsInInheritedProviderUpdate = _debugPreviousIsInInheritedProviderUpdate!; return true; }()); } _didBuild = true; } return _controller as T; } @override void dispose() { super.dispose(); if (_didBuild) { delegate.dispose?.call(element!, _controller as T); } } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); if (isLoaded) { properties ..add(DiagnosticsProperty('controller', controller)) ..add(DiagnosticsProperty('value', value)); } else { properties ..add( FlagProperty( 'controller', value: true, showName: true, ifTrue: '', ), ) ..add( FlagProperty( 'value', value: true, showName: true, ifTrue: '', ), ); } } } class _ValueDeferredInheritedProvider extends _DeferredDelegate { _ValueDeferredInheritedProvider( this.value, UpdateShouldNotify? updateShouldNotify, DeferredStartListening startListening, ) : super(updateShouldNotify, startListening); final T value; @override _ValueDeferredInheritedProviderState createState() { return _ValueDeferredInheritedProviderState(); } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty('controller', value)); } } class _ValueDeferredInheritedProviderState extends _DeferredDelegateState< T, R, _ValueDeferredInheritedProvider> { @override bool willUpdateDelegate(_ValueDeferredInheritedProvider oldDelegate) { if (delegate.value != oldDelegate.value) { if (_removeListener != null) { _removeListener!(); _removeListener = null; } return true; } return false; } @override T get controller => delegate.value; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); if (_removeListener != null) { properties.add(DiagnosticsProperty('value', value)); } else { properties.add( FlagProperty( 'value', value: true, showName: true, ifTrue: '', ), ); } } }