// /!\ DO NOT MOVE THIS FILE /!\ // // Flutter's devtool rely on the path to this file to be able to communicate with `provider`. import 'dart:developer' as developer; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:nested/nested.dart'; import 'reassemble_handler.dart'; part 'deferred_inherited_provider.dart'; part 'devtool.dart'; part 'inherited_provider.dart'; /// Whether the runtime has null safe sound mode enabled. /// /// In sound mode, all code is null safe and null safety is enforced everywhere. /// Nullability in generics is also enforced, which is how this code detects /// sound mode. /// /// In unsound mode, there can be a mix of null safe and legacy code. Some null /// checks are not done, and generics are not compared for null safety. final bool _isSoundMode = [] is! List; /// A provider that merges multiple providers into a single linear widget tree. /// It is used to improve readability and reduce boilerplate code of having to /// nest multiple layers of providers. /// /// As such, we're going from: /// /// ```dart /// Provider( /// create: (_) => Something(), /// child: Provider( /// create: (_) => SomethingElse(), /// child: Provider( /// create: (_) => AnotherThing(), /// child: someWidget, /// ), /// ), /// ), /// ``` /// /// To: /// /// ```dart /// MultiProvider( /// providers: [ /// Provider(create: (_) => Something()), /// Provider(create: (_) => SomethingElse()), /// Provider(create: (_) => AnotherThing()), /// ], /// child: someWidget, /// ) /// ``` /// /// The widget tree representation of the two approaches are identical. class MultiProvider extends Nested { /// Build a tree of providers from a list of [SingleChildWidget]. /// /// The parameter `builder` is syntactic sugar for obtaining a [BuildContext] that can /// read the providers created. /// /// This code: /// /// ```dart /// MultiProvider( /// providers: [ /// Provider(create: (_) => Something()), /// Provider(create: (_) => SomethingElse()), /// Provider(create: (_) => AnotherThing()), /// ], /// builder: (context, child) { /// final something = context.watch(); /// return Text('$something'); /// }, /// ) /// ``` /// /// is strictly equivalent to: /// /// ```dart /// MultiProvider( /// providers: [ /// Provider(create: (_) => Something()), /// Provider(create: (_) => SomethingElse()), /// Provider(create: (_) => AnotherThing()), /// ], /// child: Builder( /// builder: (context) { /// final something = context.watch(); /// return Text('$something'); /// }, /// ), /// ) /// ``` /// /// If the some provider in `providers` has a child, this will be ignored. /// /// This code: /// ```dart /// MultiProvider( /// providers: [ /// Provider(create: (_) => Something(), child: SomeWidget()), /// ], /// child: Text('Something'), /// ) /// ``` /// is equivalent to: /// /// ```dart /// MultiProvider( /// providers: [ /// Provider(create: (_) => Something()), /// ], /// child: Text('Something'), /// ) /// ``` /// /// For an explanation on the `child` parameter that `builder` receives, /// see the "Performance optimizations" section of [AnimatedBuilder]. MultiProvider({ Key? key, required List providers, Widget? child, TransitionBuilder? builder, }) : super( key: key, children: _collapseProviders(providers), child: builder != null ? Builder( builder: (context) => builder(context, child), ) : child, ); static List _collapseProviders( List providers, ) { // Merge InheritedProviders together Widget Function(Widget? child)? previous; var i = 0; for (; i < providers.length; i++) { final provider = providers[i]; if (provider is InheritedProvider) { final p = previous; final builder = p == null ? (Widget? child) => provider._buildWithChild(child, key: provider.key) : (Widget? child) { return p(provider._buildWithChild(child, key: provider.key)); }; previous = builder; } else { break; } } return [ if (previous != null) SingleChildBuilder( builder: (context, child) => previous!(child), ), if (i < providers.length) ...providers.sublist(i), ]; } } /// A [Provider] that manages the lifecycle of the value it provides by /// delegating to a pair of [Create] and [Dispose]. /// /// It is usually used to avoid making a [StatefulWidget] for something trivial, /// such as instantiating a BLoC. /// /// [Provider] is the equivalent of a [State.initState] combined with /// [State.dispose]. [Create] is called only once in [State.initState]. /// We cannot use [InheritedWidget] as it requires the value to be /// constructor-initialized and final. /// /// The following example instantiates a `Model` once, and disposes it when /// [Provider] is removed from the tree. /// /// ```dart /// class Model { /// void dispose() {} /// } /// /// class Stateless extends StatelessWidget { /// @override /// Widget build(BuildContext context) { /// return Provider( /// create: (context) => Model(), /// dispose: (context, value) => value.dispose(), /// child: ..., /// ); /// } /// } /// ``` /// /// It is worth noting that the `create` callback is lazily called. /// It is called the first time the value is read, instead of the first time /// [Provider] is inserted in the widget tree. /// /// This behavior can be disabled by passing `lazy: false` to [Provider]. /// /// ## Testing /// /// When testing widgets that consumes providers, it is necessary to /// add the proper providers in the widget tree above the tested widget. /// /// A typical test may look like this: /// /// ```dart /// final foo = MockFoo(); /// /// await tester.pumpWidget( /// Provider.value( /// value: foo, /// child: TestedWidget(), /// ), /// ); /// ``` /// /// Note this example purposefully specified the object type, instead of having /// it inferred. /// Since we used a mocked class (typically using `mockito`), then we have to /// downcast the mock to the type of the mocked class. /// Otherwise, the type inference will resolve to `Provider` instead of /// `Provider`, which will cause `Provider.of` to fail. class Provider extends InheritedProvider { /// Creates a value, store it, and expose it to its descendants. /// /// The value can be optionally disposed using [dispose] callback. /// This callback which will be called when [Provider] is unmounted from the /// widget tree. Provider({ Key? key, required Create create, Dispose? dispose, bool? lazy, TransitionBuilder? builder, Widget? child, }) : super( key: key, lazy: lazy, builder: builder, create: create, dispose: dispose, debugCheckInvalidValueType: kReleaseMode ? null : (T value) => Provider.debugCheckInvalidValueType?.call(value), child: child, ); /// Expose an existing value without disposing it. /// /// {@template provider.updateshouldnotify} /// `updateShouldNotify` can optionally be passed to avoid unnecessarily /// rebuilding dependents when [Provider] is rebuilt but `value` did not change. /// /// Defaults to `(previous, next) => previous != next`. /// See [InheritedWidget.updateShouldNotify] for more information. /// {@endtemplate} Provider.value({ Key? key, required T value, UpdateShouldNotify? updateShouldNotify, TransitionBuilder? builder, Widget? child, }) : assert(() { Provider.debugCheckInvalidValueType?.call(value); return true; }()), super.value( key: key, builder: builder, value: value, updateShouldNotify: updateShouldNotify, child: child, ); /// Obtains the nearest [Provider] up its widget tree and returns its /// value. /// /// If [listen] is `true`, later value changes will trigger a new /// [State.build] to widgets, and [State.didChangeDependencies] for /// [StatefulWidget]. /// /// `listen: false` is necessary to be able to call `Provider.of` inside /// [State.initState] or the `create` method of providers like so: /// /// ```dart /// Provider( /// create: (context) { /// return Model(Provider.of(context, listen: false)), /// }, /// ) /// ``` static T of(BuildContext context, {bool listen = true}) { 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(context); if (listen) { // bind context with the element // We have to use this method instead of dependOnInheritedElement, because // dependOnInheritedElement does not support relocating using GlobalKey // if no provider were found previously. context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope>(); } final value = inheritedElement?.value; if (_isSoundMode) { if (value is! T) { throw ProviderNullException(T, context.widget.runtimeType); } return value; } return value as T; } static _InheritedProviderScopeElement? _inheritedElementOf( BuildContext context, ) { // ignore: unnecessary_null_comparison, can happen if the application depends on a non-migrated code 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. 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. ''', ); final inheritedElement = context.getElementForInheritedWidgetOfExactType< _InheritedProviderScope>() as _InheritedProviderScopeElement?; if (inheritedElement == null && null is! T) { throw ProviderNotFoundException(T, context.widget.runtimeType); } return inheritedElement; } /// A sanity check to prevent misuse of [Provider] when a variant should be /// used instead. /// /// By default, [debugCheckInvalidValueType] will throw if `value` is a /// [Listenable] or a [Stream]. In release mode, [debugCheckInvalidValueType] /// does nothing. /// /// You can override the default behavior by "decorating" the default function.\ /// For example if you want to allow rxdart's `Subject` to work on [Provider], then /// you could do: /// /// ```dart /// void main() { /// final previous = Provider.debugCheckInvalidValueType; /// Provider.debugCheckInvalidValueType = (value) { /// if (value is Subject) return; /// previous(value); /// }; /// /// // ... /// } /// ``` /// /// This will allow `Subject`, but still allow [Stream]/[Listenable]. /// /// Alternatively you can disable this check entirely by setting /// [debugCheckInvalidValueType] to `null`: /// /// ```dart /// void main() { /// Provider.debugCheckInvalidValueType = null; /// runApp(MyApp()); /// } /// ``` // ignore: prefer_function_declarations_over_variables, false positive static void Function(T value)? debugCheckInvalidValueType = (T value) { assert(() { if (value is Listenable || value is Stream) { throw FlutterError(''' Tried to use Provider with a subtype of Listenable/Stream ($T). This is likely a mistake, as Provider will not automatically update dependents when $T is updated. Instead, consider changing Provider for more specific implementation that handles the update mechanism, such as: - ListenableProvider - ChangeNotifierProvider - ValueListenableProvider - StreamProvider Alternatively, if you are making your own provider, consider using InheritedProvider. If you think that this is not an error, you can disable this check by setting Provider.debugCheckInvalidValueType to `null` in your main file: ``` void main() { Provider.debugCheckInvalidValueType = null; runApp(MyApp()); } ``` '''); } return true; }()); }; } /// Called `Provider.of` instead of `Provider.of` but the provider /// returned `null`. class ProviderNullException implements Exception { /// Create a ProviderNullException error with the type represented as a String. ProviderNullException(this.valueType, this.widgetType); /// The type of the value being retrieved final Type valueType; /// The type of the Widget requesting the value final Type widgetType; @override String toString() { if (kReleaseMode) { return 'A provider for $valueType unexpectedly returned null.'; } return ''' Error: The widget $widgetType tried to read Provider<$valueType> but the matching provider returned null. To fix the error, consider changing Provider<$valueType> to Provider<$valueType?>. '''; } } /// The error that will be thrown if [Provider.of] fails to find a [Provider] /// as an ancestor of the [BuildContext] used. class ProviderNotFoundException implements Exception { /// Create a ProviderNotFound error with the type represented as a String. ProviderNotFoundException( this.valueType, this.widgetType, ); /// The type of the value being retrieved final Type valueType; /// The type of the Widget requesting the value final Type widgetType; @override String toString() { if (kReleaseMode) { return 'Provider<$valueType> not found for $widgetType'; } return ''' Error: Could not find the correct Provider<$valueType> above this $widgetType Widget This happens because you used a `BuildContext` that does not include the provider of your choice. There are a few common scenarios: - You added a new provider in your `main.dart` and performed a hot-reload. To fix, perform a hot-restart. - The provider you are trying to read is in a different route. Providers are "scoped". So if you insert of provider inside a route, then other routes will not be able to access that provider. - You used a `BuildContext` that is an ancestor of the provider you are trying to read. Make sure that $widgetType is under your MultiProvider/Provider<$valueType>. This usually happens when you are creating a provider and trying to read it immediately. For example, instead of: ``` Widget build(BuildContext context) { return Provider( create: (_) => Example(), // Will throw a ProviderNotFoundError, because `context` is associated // to the widget that is the parent of `Provider` child: Text(context.watch().toString()), ); } ``` consider using `builder` like so: ``` Widget build(BuildContext context) { return Provider( create: (_) => Example(), // we use `builder` to obtain a new `BuildContext` that has access to the provider builder: (context, child) { // No longer throws return Text(context.watch().toString()); } ); } ``` If none of these solutions work, consider asking for help on StackOverflow: https://stackoverflow.com/questions/tagged/flutter '''; } } /// Exposes the [read] method. extension ReadContext on BuildContext { /// Obtain a value from the nearest ancestor provider of type [T]. /// /// This method is the opposite of [watch].\ /// It will _not_ make widget rebuild when the value changes and should not be /// called inside [StatelessWidget.build]/[State.build].\ /// On the other hand, it can be freely called _outside_ of these methods. /// /// If that is incompatible with your criteria, consider using `Provider.of(context, listen: false)`.\ /// It does the same thing, but without these added restrictions (but unsafe). /// /// **DON'T** call [read] inside build if the value is used only for events: /// /// ```dart /// Widget build(BuildContext context) { /// // counter is used only for the onPressed of RaisedButton /// final counter = context.read(); /// /// return RaisedButton( /// onPressed: () => counter.increment(), /// ); /// } /// ``` /// /// While this code is not bugged in itself, this is an anti-pattern. /// It could easily lead to bugs in the future after refactoring the widget /// to use `counter` for other things, but forget to change [read] into [watch]. /// /// **CONSIDER** calling [read] inside event handlers: /// /// ```dart /// Widget build(BuildContext context) { /// return RaisedButton( /// onPressed: () { /// // as performant as the previous solution, but resilient to refactoring /// context.read().increment(), /// }, /// ); /// } /// ``` /// /// This has the same efficiency as the previous anti-pattern, but does not /// suffer from the drawback of being brittle. /// /// **DON'T** use [read] for creating widgets with a value that never changes /// /// ```dart /// Widget build(BuildContext context) { /// // using read because we only use a value that never changes. /// final model = context.read(); /// /// return Text('${model.valueThatNeverChanges}'); /// } /// ``` /// /// While the idea of not rebuilding the widget if something else changes is /// good, this should not be done with [read]. /// Relying on [read] for optimisations is very brittle and dependent /// on an implementation detail. /// /// **CONSIDER** using [select] for filtering unwanted rebuilds /// /// ```dart /// Widget build(BuildContext context) { /// // Using select to listen only to the value that used /// final valueThatNeverChanges = context.select((Model model) => model.valueThatNeverChanges); /// /// return Text('$valueThatNeverChanges'); /// } /// ``` /// /// While more verbose than [read], using [select] is a lot safer. /// It does not rely on implementation details on `Model`, and it makes /// impossible to have a bug where our UI does not refresh. /// /// ## Using [read] to simplify objects depending on other objects /// /// This method can be freely passed to objects, so that they can read providers /// without having a reference on a [BuildContext]. /// /// For example, instead of: /// /// ```dart /// class Model { /// Model(this.context); /// /// final BuildContext context; /// /// void method() { /// print(Provider.of(context)); /// } /// } /// /// // ... /// /// Provider( /// create: (context) => Model(context), /// child: ..., /// ) /// ``` /// /// we will prefer to write: /// /// ```dart /// class Model { /// Model(this.read); /// /// // `Locator` is a typedef that matches the type of `read` /// final Locator read; /// /// void method() { /// print(read()); /// } /// } /// /// // ... /// /// Provider( /// create: (context) => Model(context.read), /// child: ..., /// ) /// ``` /// /// Both snippets behaves the same. But in the second snippet, `Model` has no dependency /// on Flutter/[BuildContext]/provider. /// /// See also: /// /// - [WatchContext] and its `watch` method, similar to [read], but /// will make the widget tree rebuild when the obtained value changes. /// - [Locator], a typedef to make it easier to pass [read] to objects. T read() { return Provider.of(this, listen: false); } } /// Exposes the [watch] method. extension WatchContext on BuildContext { /// Obtain a value from the nearest ancestor provider of type [T] or [T?], and subscribe /// to the provider. /// /// If [T] is nullable and no matching providers are found, [watch] will /// return `null`. Otherwise if [T] is non-nullable, will throw [ProviderNotFoundException]. /// If [T] is non-nullable and the provider obtained returned `null`, will /// throw [ProviderNullException]. /// /// This allows widgets to optionally depend on a provider: /// /// ```dart /// runApp( /// Builder(builder: (context) { /// final movie = context.watch(); /// /// if (movie == null) Text('no Movie found'); /// return Text(movie.title); /// }), /// ); /// ``` /// /// Calling this method is equivalent to calling: /// /// ```dart /// Provider.of(context) /// ``` /// /// This method is accessible only inside [StatelessWidget.build] and /// [State.build].\ /// If you need to use it outside of these methods, consider using [Provider.of] /// instead, which doesn't have this restriction.\ /// The only exception to this rule is Providers's `update` method. /// /// See also: /// /// - [ReadContext] and its `read` method, similar to [watch], but doesn't make /// widgets rebuild if the value obtained changes. T watch() { return Provider.of(this); } } /// A generic function that can be called to read providers, without having a /// reference on [BuildContext]. /// /// It is typically a reference to the `read` [BuildContext] extension: /// /// ```dart /// BuildContext context; /// Locator locator = context.read; /// ``` /// /// This function typedef Locator = T Function();