Documentation Updated
1 |
import 'dart:async'; |
|
2 |
|
|
3 |
import 'package:collection/collection.dart'; |
|
4 |
import 'package:flutter/foundation.dart'; |
|
5 |
import 'package:flutter/widgets.dart'; |
|
6 |
import 'package:nested/nested.dart'; |
|
7 |
import 'package:flutter/rendering.dart'; |
|
8 |
import 'package:flutter/scheduler.dart'; |
|
9 |
|
|
10 |
import 'reassemble_handler.dart'; |
|
11 |
|
|
12 |
part 'inherited_provider.dart'; |
|
13 |
part 'deferred_inherited_provider.dart'; |
|
14 |
|
|
15 |
/// A provider that merges multiple providers into a single linear widget tree.
|
|
16 |
/// It is used to improve readability and reduce boilerplate code of having to
|
|
17 |
/// nest multiple layers of providers.
|
|
18 |
///
|
|
19 |
/// As such, we're going from:
|
|
20 |
///
|
|
21 |
/// ```dart
|
|
22 |
/// Provider<Something>(
|
|
23 |
/// create: (_) => Something(),
|
|
24 |
/// child: Provider<SomethingElse>(
|
|
25 |
/// create: (_) => SomethingElse(),
|
|
26 |
/// child: Provider<AnotherThing>(
|
|
27 |
/// create: (_) => AnotherThing(),
|
|
28 |
/// child: someWidget,
|
|
29 |
/// ),
|
|
30 |
/// ),
|
|
31 |
/// ),
|
|
32 |
/// ```
|
|
33 |
///
|
|
34 |
/// To:
|
|
35 |
///
|
|
36 |
/// ```dart
|
|
37 |
/// MultiProvider(
|
|
38 |
/// providers: [
|
|
39 |
/// Provider<Something>(create: (_) => Something()),
|
|
40 |
/// Provider<SomethingElse>(create: (_) => SomethingElse()),
|
|
41 |
/// Provider<AnotherThing>(create: (_) => AnotherThing()),
|
|
42 |
/// ],
|
|
43 |
/// child: someWidget,
|
|
44 |
/// )
|
|
45 |
/// ```
|
|
46 |
///
|
|
47 |
/// The widget tree representation of the two approaches are identical.
|
|
48 |
class MultiProvider extends Nested { |
|
49 |
/// Build a tree of providers from a list of [SingleChildWidget].
|
|
50 |
///
|
|
51 |
/// The parameter `builder` is syntactic sugar for obtaining a [BuildContext] that can
|
|
52 |
/// read the providers created.
|
|
53 |
///
|
|
54 |
/// This code:
|
|
55 |
///
|
|
56 |
/// ```dart
|
|
57 |
/// MultiProvider(
|
|
58 |
/// providers: [
|
|
59 |
/// Provider<Something>(create: (_) => Something()),
|
|
60 |
/// Provider<SomethingElse>(create: (_) => SomethingElse()),
|
|
61 |
/// Provider<AnotherThing>(create: (_) => AnotherThing()),
|
|
62 |
/// ],
|
|
63 |
/// builder: (context, child) {
|
|
64 |
/// final something = context.watch<Something>();
|
|
65 |
/// return Text('$something');
|
|
66 |
/// },
|
|
67 |
/// )
|
|
68 |
/// ```
|
|
69 |
///
|
|
70 |
/// is strictly equivalent to:
|
|
71 |
///
|
|
72 |
/// ```dart
|
|
73 |
/// MultiProvider(
|
|
74 |
/// providers: [
|
|
75 |
/// Provider<Something>(create: (_) => Something()),
|
|
76 |
/// Provider<SomethingElse>(create: (_) => SomethingElse()),
|
|
77 |
/// Provider<AnotherThing>(create: (_) => AnotherThing()),
|
|
78 |
/// ],
|
|
79 |
/// child: Builder(
|
|
80 |
/// builder: (context) {
|
|
81 |
/// final something = context.watch<Something>();
|
|
82 |
/// return Text('$something');
|
|
83 |
/// },
|
|
84 |
/// ),
|
|
85 |
/// )
|
|
86 |
/// ```
|
|
87 |
///
|
|
88 |
/// For an explanation on the `child` parameter that `builder` receives,
|
|
89 |
/// see the "Performance optimizations" section of [AnimatedBuilder].
|
|
90 | 3 |
MultiProvider({ |
91 |
Key key, |
|
92 |
@required List<SingleChildWidget> providers, |
|
93 |
Widget child, |
|
94 |
TransitionBuilder builder, |
|
95 | 3 |
}) : assert(providers != null), |
96 | 3 |
super( |
97 |
key: key, |
|
98 |
children: providers, |
|
99 |
child: builder != null |
|
100 | 3 |
? Builder( |
101 | 3 |
builder: (context) => builder(context, child), |
102 |
)
|
|
103 |
: child, |
|
104 |
);
|
|
105 |
}
|
|
106 |
|
|
107 |
/// A [Provider] that manages the lifecycle of the value it provides by
|
|
108 |
/// delegating to a pair of [Create] and [Dispose].
|
|
109 |
///
|
|
110 |
/// It is usually used to avoid making a [StatefulWidget] for something trivial,
|
|
111 |
/// such as instantiating a BLoC.
|
|
112 |
///
|
|
113 |
/// [Provider] is the equivalent of a [State.initState] combined with
|
|
114 |
/// [State.dispose]. [Create] is called only once in [State.initState].
|
|
115 |
/// We cannot use [InheritedWidget] as it requires the value to be
|
|
116 |
/// constructor-initialized and final.
|
|
117 |
///
|
|
118 |
/// The following example instantiates a `Model` once, and disposes it when
|
|
119 |
/// [Provider] is removed from the tree.
|
|
120 |
///
|
|
121 |
/// ```dart
|
|
122 |
/// class Model {
|
|
123 |
/// void dispose() {}
|
|
124 |
/// }
|
|
125 |
///
|
|
126 |
/// class Stateless extends StatelessWidget {
|
|
127 |
/// @override
|
|
128 |
/// Widget build(BuildContext context) {
|
|
129 |
/// return Provider<Model>(
|
|
130 |
/// create: (context) => Model(),
|
|
131 |
/// dispose: (context, value) => value.dispose(),
|
|
132 |
/// child: ...,
|
|
133 |
/// );
|
|
134 |
/// }
|
|
135 |
/// }
|
|
136 |
/// ```
|
|
137 |
///
|
|
138 |
/// It is worth noting that the `create` callback is lazily called.
|
|
139 |
/// It is called the first time the value is read, instead of the first time
|
|
140 |
/// [Provider] is inserted in the widget tree.
|
|
141 |
///
|
|
142 |
/// This behavior can be disabled by passing `lazy: false` to [Provider].
|
|
143 |
///
|
|
144 |
/// ## Testing
|
|
145 |
///
|
|
146 |
/// When testing widgets that consumes providers, it is necessary to
|
|
147 |
/// add the proper providers in the widget tree above the tested widget.
|
|
148 |
///
|
|
149 |
/// A typical test may look like this:
|
|
150 |
///
|
|
151 |
/// ```dart
|
|
152 |
/// final foo = MockFoo();
|
|
153 |
///
|
|
154 |
/// await tester.pumpWidget(
|
|
155 |
/// Provider<Foo>.value(
|
|
156 |
/// value: foo,
|
|
157 |
/// child: TestedWidget(),
|
|
158 |
/// ),
|
|
159 |
/// );
|
|
160 |
/// ```
|
|
161 |
///
|
|
162 |
/// Note this example purposefully specified the object type, instead of having
|
|
163 |
/// it inferred.
|
|
164 |
/// Since we used a mocked class (typically using `mockito`), then we have to
|
|
165 |
/// downcast the mock to the type of the mocked class.
|
|
166 |
/// Otherwise, the type inference will resolve to `Provider<MockFoo>` instead of
|
|
167 |
/// `Provider<Foo>`, which will cause `Provider.of<Foo>` to fail.
|
|
168 |
class Provider<T> extends InheritedProvider<T> { |
|
169 |
/// Creates a value, store it, and expose it to its descendants.
|
|
170 |
///
|
|
171 |
/// The value can be optionally disposed using [dispose] callback.
|
|
172 |
/// This callback which will be called when [Provider] is unmounted from the
|
|
173 |
/// widget tree.
|
|
174 | 3 |
Provider({ |
175 |
Key key, |
|
176 |
@required Create<T> create, |
|
177 |
Dispose<T> dispose, |
|
178 |
bool lazy, |
|
179 |
TransitionBuilder builder, |
|
180 |
Widget child, |
|
181 | 3 |
}) : assert(create != null), |
182 | 3 |
super( |
183 |
key: key, |
|
184 |
lazy: lazy, |
|
185 |
builder: builder, |
|
186 |
create: create, |
|
187 |
dispose: dispose, |
|
188 |
debugCheckInvalidValueType: kReleaseMode |
|
189 |
? null |
|
190 | 3 |
: (T value) => |
191 | 3 |
Provider.debugCheckInvalidValueType?.call<T>(value), |
192 |
child: child, |
|
193 |
);
|
|
194 |
|
|
195 |
/// Expose an existing value without disposing it.
|
|
196 |
///
|
|
197 |
/// {@template provider.updateshouldnotify}
|
|
198 |
/// `updateShouldNotify` can optionally be passed to avoid unnecessarily
|
|
199 |
/// rebuilding dependents when [Provider] is rebuilt but `value` did not change.
|
|
200 |
///
|
|
201 |
/// Defaults to `(previous, next) => previous != next`.
|
|
202 |
/// See [InheritedWidget.updateShouldNotify] for more information.
|
|
203 |
/// {@endtemplate}
|
|
204 | 3 |
Provider.value({ |
205 |
Key key, |
|
206 |
@required T value, |
|
207 |
UpdateShouldNotify<T> updateShouldNotify, |
|
208 |
TransitionBuilder builder, |
|
209 |
Widget child, |
|
210 | 3 |
}) : assert(() { |
211 | 3 |
Provider.debugCheckInvalidValueType?.call<T>(value); |
212 |
return true; |
|
213 | 3 |
}()),
|
214 | 3 |
super.value( |
215 |
key: key, |
|
216 |
builder: builder, |
|
217 |
value: value, |
|
218 |
updateShouldNotify: updateShouldNotify, |
|
219 |
child: child, |
|
220 |
);
|
|
221 |
|
|
222 |
/// Obtains the nearest [Provider<T>] up its widget tree and returns its
|
|
223 |
/// value.
|
|
224 |
///
|
|
225 |
/// If [listen] is `true`, later value changes will trigger a new
|
|
226 |
/// [State.build] to widgets, and [State.didChangeDependencies] for
|
|
227 |
/// [StatefulWidget].
|
|
228 |
///
|
|
229 |
/// `listen: false` is necessary to be able to call `Provider.of` inside
|
|
230 |
/// [State.initState] or the `create` method of providers like so:
|
|
231 |
///
|
|
232 |
/// ```dart
|
|
233 |
/// Provider(
|
|
234 |
/// create: (context) {
|
|
235 |
/// return Model(Provider.of<Something>(context, listen: false)),
|
|
236 |
/// },
|
|
237 |
/// )
|
|
238 |
/// ```
|
|
239 | 3 |
static T of<T>(BuildContext context, {bool listen = true}) { |
240 | 3 |
assert(context != null); |
241 |
assert( |
|
242 | 3 |
context.owner.debugBuilding || |
243 | 3 |
listen == false || |
244 |
debugIsInInheritedProviderUpdate, |
|
245 |
'''
|
|
246 |
Tried to listen to a value exposed with provider, from outside of the widget tree.
|
|
247 |
|
|
248 |
This is likely caused by an event handler (like a button's onPressed) that called
|
|
249 |
Provider.of without passing `listen: false`.
|
|
250 |
|
|
251 |
To fix, write:
|
|
252 |
Provider.of<$T>(context, listen: false); |
|
253 |
|
|
254 |
It is unsupported because may pointlessly rebuild the widget associated to the
|
|
255 |
event handler, when the widget tree doesn't care about the value.
|
|
256 |
|
|
257 |
The context used was: $context |
|
258 | 3 |
''', |
259 |
);
|
|
260 |
|
|
261 | 3 |
final inheritedElement = _inheritedElementOf<T>(context); |
262 |
|
|
263 |
if (listen) { |
|
264 | 3 |
context.dependOnInheritedElement(inheritedElement); |
265 |
}
|
|
266 |
|
|
267 | 3 |
return inheritedElement.value; |
268 |
}
|
|
269 |
|
|
270 | 3 |
static _InheritedProviderScopeElement<T> _inheritedElementOf<T>( |
271 |
BuildContext context, |
|
272 |
) { |
|
273 |
assert(context != null, ''' |
|
274 |
Tried to call context.read/watch/select or similar on a `context` that is null.
|
|
275 |
|
|
276 |
This can happen if you used the context of a StatefulWidget and that
|
|
277 |
StatefulWidget was disposed.
|
|
278 |
'''); |
|
279 |
assert( |
|
280 | 3 |
_debugIsSelecting == false, |
281 |
'Cannot call context.read/watch/select inside the callback of a context.select', |
|
282 |
);
|
|
283 |
assert( |
|
284 | 3 |
T != dynamic, |
285 |
'''
|
|
286 |
Tried to call Provider.of<dynamic>. This is likely a mistake and is therefore
|
|
287 |
unsupported.
|
|
288 |
|
|
289 |
If you want to expose a variable that can be anything, consider changing
|
|
290 |
`dynamic` to `Object` instead.
|
|
291 |
''', |
|
292 |
);
|
|
293 |
_InheritedProviderScopeElement<T> inheritedElement; |
|
294 |
|
|
295 | 3 |
if (context.widget is _InheritedProviderScope<T>) { |
296 |
// An InheritedProvider<T>'s update tries to obtain a parent provider of
|
|
297 |
// the same type.
|
|
298 | 3 |
context.visitAncestorElements((parent) { |
299 | 3 |
inheritedElement = parent.getElementForInheritedWidgetOfExactType< |
300 |
_InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>; |
|
301 |
return false; |
|
302 |
});
|
|
303 |
} else { |
|
304 | 3 |
inheritedElement = context.getElementForInheritedWidgetOfExactType< |
305 |
_InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>; |
|
306 |
}
|
|
307 |
|
|
308 |
if (inheritedElement == null) { |
|
309 | 3 |
throw ProviderNotFoundException(T, context.widget.runtimeType); |
310 |
}
|
|
311 |
|
|
312 |
return inheritedElement; |
|
313 |
}
|
|
314 |
|
|
315 |
/// A sanity check to prevent misuse of [Provider] when a variant should be
|
|
316 |
/// used instead.
|
|
317 |
///
|
|
318 |
/// By default, [debugCheckInvalidValueType] will throw if `value` is a
|
|
319 |
/// [Listenable] or a [Stream]. In release mode, [debugCheckInvalidValueType]
|
|
320 |
/// does nothing.
|
|
321 |
///
|
|
322 |
/// You can override the default behavior by "decorating" the default function.\
|
|
323 |
/// For example if you want to allow rxdart's `Subject` to work on [Provider], then
|
|
324 |
/// you could do:
|
|
325 |
///
|
|
326 |
/// ```dart
|
|
327 |
/// void main() {
|
|
328 |
/// final previous = Provider.debugCheckInvalidValueType;
|
|
329 |
/// Provider.debugCheckInvalidValueType = <T>(value) {
|
|
330 |
/// if (value is Subject) return;
|
|
331 |
/// previous<T>(value);
|
|
332 |
/// };
|
|
333 |
///
|
|
334 |
/// // ...
|
|
335 |
/// }
|
|
336 |
/// ```
|
|
337 |
///
|
|
338 |
/// This will allow `Subject`, but still allow [Stream]/[Listenable].
|
|
339 |
///
|
|
340 |
/// Alternatively you can disable this check entirely by setting
|
|
341 |
/// [debugCheckInvalidValueType] to `null`:
|
|
342 |
///
|
|
343 |
/// ```dart
|
|
344 |
/// void main() {
|
|
345 |
/// Provider.debugCheckInvalidValueType = null;
|
|
346 |
/// runApp(MyApp());
|
|
347 |
/// }
|
|
348 |
/// ```
|
|
349 | 3 |
static void Function<T>(T value) debugCheckInvalidValueType = <T>(T value) { |
350 | 3 |
assert(() { |
351 | 3 |
if (value is Listenable || value is Stream) { |
352 | 3 |
throw FlutterError(''' |
353 |
Tried to use Provider with a subtype of Listenable/Stream ($T). |
|
354 |
|
|
355 |
This is likely a mistake, as Provider will not automatically update dependents
|
|
356 |
when $T is updated. Instead, consider changing Provider for more specific |
|
357 |
implementation that handles the update mechanism, such as:
|
|
358 |
|
|
359 |
- ListenableProvider
|
|
360 |
- ChangeNotifierProvider
|
|
361 |
- ValueListenableProvider
|
|
362 |
- StreamProvider
|
|
363 |
|
|
364 |
Alternatively, if you are making your own provider, consider using InheritedProvider.
|
|
365 |
|
|
366 |
If you think that this is not an error, you can disable this check by setting
|
|
367 |
Provider.debugCheckInvalidValueType to `null` in your main file:
|
|
368 |
|
|
369 |
```
|
|
370 |
void main() {
|
|
371 |
Provider.debugCheckInvalidValueType = null;
|
|
372 |
|
|
373 |
runApp(MyApp());
|
|
374 |
}
|
|
375 |
```
|
|
376 | 3 |
'''); |
377 |
}
|
|
378 |
return true; |
|
379 | 3 |
}());
|
380 |
};
|
|
381 |
}
|
|
382 |
|
|
383 |
/// The error that will be thrown if [Provider.of] fails to find a [Provider]
|
|
384 |
/// as an ancestor of the [BuildContext] used.
|
|
385 |
class ProviderNotFoundException implements Exception { |
|
386 |
/// The type of the value being retrieved
|
|
387 |
final Type valueType; |
|
388 |
|
|
389 |
/// The type of the Widget requesting the value
|
|
390 |
final Type widgetType; |
|
391 |
|
|
392 |
/// Create a ProviderNotFound error with the type represented as a String.
|
|
393 | 3 |
ProviderNotFoundException( |
394 |
this.valueType, |
|
395 |
this.widgetType, |
|
396 |
);
|
|
397 |
|
|
398 | 3 |
@override |
399 |
String toString() { |
|
400 |
return ''' |
|
401 | 3 |
Error: Could not find the correct Provider<$valueType> above this $widgetType Widget |
402 |
|
|
403 |
This likely happens because you used a `BuildContext` that does not include the provider
|
|
404 |
of your choice. There are a few common scenarios:
|
|
405 |
|
|
406 |
- The provider you are trying to read is in a different route.
|
|
407 |
|
|
408 |
Providers are "scoped". So if you insert of provider inside a route, then
|
|
409 |
other routes will not be able to access that provider.
|
|
410 |
|
|
411 |
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
|
|
412 |
|
|
413 | 3 |
Make sure that $widgetType is under your MultiProvider/Provider<$valueType>. |
414 |
This usually happen when you are creating a provider and trying to read it immediately.
|
|
415 |
|
|
416 |
For example, instead of:
|
|
417 |
|
|
418 |
```
|
|
419 |
Widget build(BuildContext context) {
|
|
420 |
return Provider<Example>(
|
|
421 |
create: (_) => Example(),
|
|
422 |
// Will throw a ProviderNotFoundError, because `context` is associated
|
|
423 |
// to the widget that is the parent of `Provider<Example>`
|
|
424 |
child: Text(context.watch<Example>()),
|
|
425 |
),
|
|
426 |
}
|
|
427 |
```
|
|
428 |
|
|
429 |
consider using `builder` like so:
|
|
430 |
|
|
431 |
```
|
|
432 |
Widget build(BuildContext context) {
|
|
433 |
return Provider<Example>(
|
|
434 |
create: (_) => Example(),
|
|
435 |
// we use `builder` to obtain a new `BuildContext` that has access to the provider
|
|
436 |
builder: (context) {
|
|
437 |
// No longer throws
|
|
438 |
return Text(context.watch<Example>()),
|
|
439 |
}
|
|
440 |
),
|
|
441 |
}
|
|
442 |
```
|
|
443 |
|
|
444 |
If none of these solutions work, consider asking for help on StackOverflow:
|
|
445 |
https://stackoverflow.com/questions/tagged/flutter
|
|
446 | 3 |
'''; |
447 |
}
|
|
448 |
}
|
|
449 |
|
|
450 |
/// Exposes the [read] method.
|
|
451 |
extension ReadContext on BuildContext { |
|
452 |
/// Obtain a value from the nearest ancestor provider of type [T].
|
|
453 |
///
|
|
454 |
/// This method is the opposite of [watch].\
|
|
455 |
/// It will _not_ make widget rebuild when the value changes and cannot be
|
|
456 |
/// called inside [StatelessWidget.build]/[State.build].\
|
|
457 |
/// On the other hand, it can be freely called _outside_ of these methods.
|
|
458 |
///
|
|
459 |
/// If that is incompatible with your criteria, consider using `Provider.of(context, listen: false)`.\
|
|
460 |
/// It does the same thing, but without these added restrictions (but unsafe).
|
|
461 |
///
|
|
462 |
/// **DON'T** call [read] inside build if the value is used only for events:
|
|
463 |
///
|
|
464 |
/// ```dart
|
|
465 |
/// Widget build(BuildContext context) {
|
|
466 |
/// // counter is used only for the onPressed of RaisedButton
|
|
467 |
/// final counter = context.read<Counter>();
|
|
468 |
///
|
|
469 |
/// return RaisedButton(
|
|
470 |
/// onPressed: () => counter.increment(),
|
|
471 |
/// );
|
|
472 |
/// }
|
|
473 |
/// ```
|
|
474 |
///
|
|
475 |
/// While this code is not bugged in itself, this is an anti-pattern.
|
|
476 |
/// It could easily lead to bugs in the future after refactoring the widget
|
|
477 |
/// to use `counter` for other things, but forget to change [read] into [watch].
|
|
478 |
///
|
|
479 |
/// **CONSIDER** calling [read] inside event handlers:
|
|
480 |
///
|
|
481 |
/// ```dart
|
|
482 |
/// Widget build(BuildContext context) {
|
|
483 |
/// return RaisedButton(
|
|
484 |
/// onPressed: () {
|
|
485 |
/// // as performant as the previous previous solution, but resilient to refactoring
|
|
486 |
/// context.read<Counter>().increment(),
|
|
487 |
/// },
|
|
488 |
/// );
|
|
489 |
/// }
|
|
490 |
/// ```
|
|
491 |
///
|
|
492 |
/// This has the same efficiency as the previous anti-pattern, but does not
|
|
493 |
/// suffer from the drawback of being brittle.
|
|
494 |
///
|
|
495 |
/// **DON'T** use [read] for creating widgets with a value that never changes
|
|
496 |
///
|
|
497 |
/// ```dart
|
|
498 |
/// Widget build(BuildContext context) {
|
|
499 |
/// // using read because we only use a value that never changes.
|
|
500 |
/// final model = context.read<Model>();
|
|
501 |
///
|
|
502 |
/// return Text('${model.valueThatNeverChanges}');
|
|
503 |
/// }
|
|
504 |
/// ```
|
|
505 |
///
|
|
506 |
/// While the idea of not rebuilding the widget if something else changes is
|
|
507 |
/// good, this should not be done with [read].
|
|
508 |
/// Relying on [read] for optimisations is very brittle and dependent
|
|
509 |
/// on an implementation detail.
|
|
510 |
///
|
|
511 |
/// **CONSIDER** using [select] for filtering unwanted rebuilds
|
|
512 |
///
|
|
513 |
/// ```dart
|
|
514 |
/// Widget build(BuildContext context) {
|
|
515 |
/// // Using select to listen only to the value that used
|
|
516 |
/// final valueThatNeverChanges = context.select((Model model) => model.valueThatNeverChanges);
|
|
517 |
///
|
|
518 |
/// return Text('$valueThatNeverChanges');
|
|
519 |
/// }
|
|
520 |
/// ```
|
|
521 |
///
|
|
522 |
/// While more verbose than [read], using [select] is a lot safer.
|
|
523 |
/// It does not rely on implementation details on `Model`, and it makes
|
|
524 |
/// impossible to have a bug where our UI does not refresh.
|
|
525 |
///
|
|
526 |
/// ## Using [read] to simplify objects depending on other objects
|
|
527 |
///
|
|
528 |
/// This method can be freely passed to objects, so that they can read providers
|
|
529 |
/// without having a reference on a [BuildContext].
|
|
530 |
///
|
|
531 |
/// For example, instead of:
|
|
532 |
///
|
|
533 |
/// ```dart
|
|
534 |
/// class Model {
|
|
535 |
/// Model(this.context);
|
|
536 |
///
|
|
537 |
/// final BuildContext context;
|
|
538 |
///
|
|
539 |
/// void method() {
|
|
540 |
/// print(Provider.of<Whatever>(context));
|
|
541 |
/// }
|
|
542 |
/// }
|
|
543 |
///
|
|
544 |
/// // ...
|
|
545 |
///
|
|
546 |
/// Provider(
|
|
547 |
/// create: (context) => Model(context),
|
|
548 |
/// child: ...,
|
|
549 |
/// )
|
|
550 |
/// ```
|
|
551 |
///
|
|
552 |
/// we will prefer to write:
|
|
553 |
///
|
|
554 |
/// ```dart
|
|
555 |
/// class Model {
|
|
556 |
/// Model(this.read);
|
|
557 |
///
|
|
558 |
/// // `Locator` is a typedef that matches the type of `read`
|
|
559 |
/// final Locator read;
|
|
560 |
///
|
|
561 |
/// void method() {
|
|
562 |
/// print(read<Whatever>());
|
|
563 |
/// }
|
|
564 |
/// }
|
|
565 |
///
|
|
566 |
/// // ...
|
|
567 |
///
|
|
568 |
/// Provider(
|
|
569 |
/// create: (context) => Model(context.read),
|
|
570 |
/// child: ...,
|
|
571 |
/// )
|
|
572 |
/// ```
|
|
573 |
///
|
|
574 |
/// Both snippets behaves the same. But in the second snippet, `Model` has no dependency
|
|
575 |
/// on Flutter/[BuildContext]/provider.
|
|
576 |
///
|
|
577 |
/// See also:
|
|
578 |
///
|
|
579 |
/// - [WatchContext] and its `watch` method, similar to [read], but
|
|
580 |
/// will make the widget tree rebuild when the obtained value changes.
|
|
581 |
/// - [Locator], a typedef to make it easier to pass [read] to objects.
|
|
582 | 3 |
T read<T>() { |
583 |
assert( |
|
584 | 3 |
debugIsInInheritedProviderCreate || |
585 | 3 |
(!debugDoingBuild && !debugIsInInheritedProviderUpdate), |
586 |
'''
|
|
587 |
Tried to use `context.read<$T>` inside either a `build` method or the `update` callback of a provider. |
|
588 |
|
|
589 |
This is unsafe to do so. Instead, consider using `context.watch<$T>`. |
|
590 |
If you used `context.read` voluntarily as a performance optimisation, the solution
|
|
591 |
is instead to use `context.select`.
|
|
592 | 3 |
'''); |
593 | 3 |
return Provider.of<T>(this, listen: false); |
594 |
}
|
|
595 |
}
|
|
596 |
|
|
597 |
/// Exposes the [watch] method.
|
|
598 |
extension WatchContext on BuildContext { |
|
599 |
/// Obtain a value from the nearest ancestor provider of type [T], and subscribe
|
|
600 |
/// to the provider.
|
|
601 |
///
|
|
602 |
/// Calling this method is equivalent to calling:
|
|
603 |
///
|
|
604 |
/// ```dart
|
|
605 |
/// Provider.of<T>(context)
|
|
606 |
/// ```
|
|
607 |
///
|
|
608 |
/// This method is accessible only inside [StatelessWidget.build] and
|
|
609 |
/// [State.build].\
|
|
610 |
/// If you need need to use it outside of these methods, consider using [Provider.of]
|
|
611 |
/// instead, which doesn't have this restriction.\
|
|
612 |
/// The only exception to this rule is Providers's `update` method.
|
|
613 |
///
|
|
614 |
/// See also:
|
|
615 |
///
|
|
616 |
/// - [ReadContext] and its `read` method, similar to [watch], but doesn't make
|
|
617 |
/// widgets rebuild if the value obtained changes.
|
|
618 | 3 |
T watch<T>() { |
619 |
assert( |
|
620 | 3 |
widget is LayoutBuilder || |
621 | 3 |
widget is SliverWithKeepAliveWidget || |
622 | 3 |
debugDoingBuild || |
623 |
debugIsInInheritedProviderUpdate, |
|
624 |
'''
|
|
625 |
Tried to use `context.watch<$T>` outside of the `build` method or `update` callback of a provider. |
|
626 |
|
|
627 |
This is likely a mistake, as it doesn't make sense to rebuild a widget when the value
|
|
628 |
obtained changes, if that value is not used to build other widgets.
|
|
629 |
|
|
630 |
Consider using `context.read<$T> instead. |
|
631 | 3 |
'''); |
632 | 3 |
return Provider.of<T>(this); |
633 |
}
|
|
634 |
}
|
|
635 |
|
|
636 |
/// A generic function that can be called to read providers, without having a
|
|
637 |
/// reference on [BuildContext].
|
|
638 |
///
|
|
639 |
/// It is typically a reference to the `read` [BuildContext] extension:
|
|
640 |
///
|
|
641 |
/// ```dart
|
|
642 |
/// BuildContext context;
|
|
643 |
/// Locator locator = context.read;
|
|
644 |
/// ```
|
|
645 |
///
|
|
646 |
/// This function
|
|
647 |
typedef Locator = T Function<T>(); |
Read our documentation on viewing source code .