| import 'package:collection/collection.dart'; |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/widgets.dart'; |
| import 'package:nested/nested.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter/scheduler.dart'; |
| |
| import 'reassemble_handler.dart'; |
| |
| part 'inherited_provider.dart'; |
| part 'deferred_inherited_provider.dart'; |
| |
| /// 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<Something>( |
| /// create: (_) => Something(), |
| /// child: Provider<SomethingElse>( |
| /// create: (_) => SomethingElse(), |
| /// child: Provider<AnotherThing>( |
| /// create: (_) => AnotherThing(), |
| /// child: someWidget, |
| /// ), |
| /// ), |
| /// ), |
| /// ``` |
| /// |
| /// To: |
| /// |
| /// ```dart |
| /// MultiProvider( |
| /// providers: [ |
| /// Provider<Something>(create: (_) => Something()), |
| /// Provider<SomethingElse>(create: (_) => SomethingElse()), |
| /// Provider<AnotherThing>(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<Something>(create: (_) => Something()), |
| /// Provider<SomethingElse>(create: (_) => SomethingElse()), |
| /// Provider<AnotherThing>(create: (_) => AnotherThing()), |
| /// ], |
| /// builder: (context, child) { |
| /// final something = context.watch<Something>(); |
| /// return Text('$something'); |
| /// }, |
| /// ) |
| /// ``` |
| /// |
| /// is strictly equivalent to: |
| /// |
| /// ```dart |
| /// MultiProvider( |
| /// providers: [ |
| /// Provider<Something>(create: (_) => Something()), |
| /// Provider<SomethingElse>(create: (_) => SomethingElse()), |
| /// Provider<AnotherThing>(create: (_) => AnotherThing()), |
| /// ], |
| /// child: Builder( |
| /// builder: (context) { |
| /// final something = context.watch<Something>(); |
| /// return Text('$something'); |
| /// }, |
| /// ), |
| /// ) |
| /// ``` |
| /// |
| /// If the some provider in `providers` has a child, this will be ignored. |
| /// |
| /// This code: |
| /// ```dart |
| /// MultiProvider( |
| /// providers: [ |
| /// Provider<Something>(create: (_) => Something(), child: SomeWidget()), |
| /// ], |
| /// child: Text('Something'), |
| /// ) |
| /// ``` |
| /// is equivalent to: |
| /// |
| /// ```dart |
| /// MultiProvider( |
| /// providers: [ |
| /// Provider<Something>(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<SingleChildWidget> providers, |
| Widget child, |
| TransitionBuilder builder, |
| }) : assert(providers != null), |
| super( |
| key: key, |
| children: providers, |
| child: builder != null |
| ? Builder( |
| builder: (context) => builder(context, child), |
| ) |
| : child, |
| ); |
| } |
| |
| /// 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<Model>( |
| /// 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<Foo>.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<MockFoo>` instead of |
| /// `Provider<Foo>`, which will cause `Provider.of<Foo>` to fail. |
| class Provider<T> extends InheritedProvider<T> { |
| /// 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<T> create, |
| Dispose<T> dispose, |
| bool lazy, |
| TransitionBuilder builder, |
| Widget child, |
| }) : assert(create != null), |
| super( |
| key: key, |
| lazy: lazy, |
| builder: builder, |
| create: create, |
| dispose: dispose, |
| debugCheckInvalidValueType: kReleaseMode |
| ? null |
| : (T value) => |
| Provider.debugCheckInvalidValueType?.call<T>(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<T> updateShouldNotify, |
| TransitionBuilder builder, |
| Widget child, |
| }) : assert(() { |
| Provider.debugCheckInvalidValueType?.call<T>(value); |
| return true; |
| }()), |
| super.value( |
| key: key, |
| builder: builder, |
| value: value, |
| updateShouldNotify: updateShouldNotify, |
| child: child, |
| ); |
| |
| /// Obtains the nearest [Provider<T>] 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<Something>(context, listen: false)), |
| /// }, |
| /// ) |
| /// ``` |
| 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; |
| } |
| |
| /// 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 = <T>(value) { |
| /// if (value is Subject) return; |
| /// previous<T>(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>(T value) debugCheckInvalidValueType = <T>(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; |
| }()); |
| }; |
| } |
| |
| /// 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() { |
| return ''' |
| Error: Could not find the correct Provider<$valueType> above this $widgetType Widget |
| |
| This likely happens because you used a `BuildContext` that does not include the provider |
| of your choice. There are a few common scenarios: |
| |
| - 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<Example>( |
| create: (_) => Example(), |
| // Will throw a ProviderNotFoundError, because `context` is associated |
| // to the widget that is the parent of `Provider<Example>` |
| child: Text(context.watch<Example>()), |
| ), |
| } |
| ``` |
| |
| consider using `builder` like so: |
| |
| ``` |
| Widget build(BuildContext context) { |
| return Provider<Example>( |
| create: (_) => Example(), |
| // we use `builder` to obtain a new `BuildContext` that has access to the provider |
| builder: (context) { |
| // No longer throws |
| return Text(context.watch<Example>()), |
| } |
| ), |
| } |
| ``` |
| |
| 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 cannot 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<Counter>(); |
| /// |
| /// 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<Counter>().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<Model>(); |
| /// |
| /// 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<Whatever>(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<Whatever>()); |
| /// } |
| /// } |
| /// |
| /// // ... |
| /// |
| /// 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<T>() { |
| assert( |
| debugIsInInheritedProviderCreate || |
| (!debugDoingBuild && !debugIsInInheritedProviderUpdate), |
| ''' |
| Tried to use `context.read<$T>` inside either a `build` method or the `update` callback of a provider. |
| |
| This is unsafe to do so. Instead, consider using `context.watch<$T>`. |
| If you used `context.read` voluntarily as a performance optimisation, the solution |
| is instead to use `context.select`. |
| '''); |
| return Provider.of<T>(this, listen: false); |
| } |
| } |
| |
| /// Exposes the [watch] method. |
| extension WatchContext on BuildContext { |
| /// Obtain a value from the nearest ancestor provider of type [T], and subscribe |
| /// to the provider. |
| /// |
| /// Calling this method is equivalent to calling: |
| /// |
| /// ```dart |
| /// Provider.of<T>(context) |
| /// ``` |
| /// |
| /// This method is accessible only inside [StatelessWidget.build] and |
| /// [State.build].\ |
| /// If you need 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<T>() { |
| assert( |
| widget is LayoutBuilder || |
| widget is SliverWithKeepAliveWidget || |
| debugDoingBuild || |
| debugIsInInheritedProviderUpdate, |
| ''' |
| Tried to use `context.watch<$T>` outside of the `build` method or `update` callback of a provider. |
| |
| This is likely a mistake, as it doesn't make sense to rebuild a widget when the value |
| obtained changes, if that value is not used to build other widgets. |
| |
| Consider using `context.read<$T> instead. |
| '''); |
| return Provider.of<T>(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<T>(); |