|
| 1 | +import 'package:flutter/widgets.dart'; |
| 2 | + |
| 3 | +/// An [InheritedWidget] that exposes a [State] of type [T] to descendants for |
| 4 | +/// O(1) ancestor lookup. |
| 5 | +/// |
| 6 | +/// Use this to back the `static of(BuildContext)` accessor of a |
| 7 | +/// [StatefulWidget] that wants to expose its [State] to descendants without |
| 8 | +/// the per-call cost of [BuildContext.findAncestorStateOfType], which walks |
| 9 | +/// the element tree on every call. |
| 10 | +/// |
| 11 | +/// {@tool snippet} |
| 12 | +/// |
| 13 | +/// Inside the widget's state, wrap the subtree in a [StreamStateScope] and |
| 14 | +/// define `of` / `maybeOf` that read it back: |
| 15 | +/// |
| 16 | +/// ```dart |
| 17 | +/// class MyWidget extends StatefulWidget { |
| 18 | +/// // ... |
| 19 | +/// static MyWidgetState of(BuildContext context) => |
| 20 | +/// StreamStateScope.of<MyWidgetState>(context); |
| 21 | +/// |
| 22 | +/// static MyWidgetState? maybeOf(BuildContext context) => |
| 23 | +/// StreamStateScope.maybeOf<MyWidgetState>(context); |
| 24 | +/// } |
| 25 | +/// |
| 26 | +/// class MyWidgetState extends State<MyWidget> { |
| 27 | +/// @override |
| 28 | +/// Widget build(BuildContext context) { |
| 29 | +/// // [T] is inferred from `state: this`, no need to spell it out. |
| 30 | +/// return StreamStateScope(state: this, child: widget.child); |
| 31 | +/// } |
| 32 | +/// } |
| 33 | +/// ``` |
| 34 | +/// {@end-tool} |
| 35 | +/// |
| 36 | +/// [updateShouldNotify] returns `false`: lookups are intended as a dependency |
| 37 | +/// -free pointer to the enclosing [State], and the [State] identity is stable |
| 38 | +/// for the life of the element. Consumers that need to react to data exposed |
| 39 | +/// by the [State] should subscribe to its streams or notifiers explicitly. |
| 40 | +class StreamStateScope<T extends State> extends InheritedWidget { |
| 41 | + /// Creates a [StreamStateScope] exposing [state] to descendants. |
| 42 | + const StreamStateScope({ |
| 43 | + super.key, |
| 44 | + required this.state, |
| 45 | + required super.child, |
| 46 | + }); |
| 47 | + |
| 48 | + /// The [State] exposed to descendants. |
| 49 | + final T state; |
| 50 | + |
| 51 | + /// Returns the [State] of type [T] from the closest enclosing |
| 52 | + /// [StreamStateScope]. |
| 53 | + /// |
| 54 | + /// Throws a [FlutterError] if no matching [StreamStateScope] is found. |
| 55 | + static T of<T extends State>(BuildContext context) { |
| 56 | + final result = maybeOf<T>(context); |
| 57 | + if (result != null) return result; |
| 58 | + |
| 59 | + throw FlutterError.fromParts(<DiagnosticsNode>[ |
| 60 | + ErrorSummary( |
| 61 | + 'StreamStateScope.of<$T>() called with a context that does not ' |
| 62 | + 'contain a StreamStateScope<$T>.', |
| 63 | + ), |
| 64 | + ErrorDescription( |
| 65 | + 'No StreamStateScope<$T> ancestor could be found starting from the ' |
| 66 | + 'context that was passed to StreamStateScope.of<$T>().', |
| 67 | + ), |
| 68 | + context.describeElement('The context used was'), |
| 69 | + ]); |
| 70 | + } |
| 71 | + |
| 72 | + /// Returns the [State] of type [T] from the closest enclosing |
| 73 | + /// [StreamStateScope], or `null` if there isn't one. |
| 74 | + /// |
| 75 | + /// This is a pure lookup — the calling element is **not** registered as a |
| 76 | + /// dependent, so the [State] identity must not change for the life of the |
| 77 | + /// scope (it doesn't, since [State.build] returns the same scope every |
| 78 | + /// frame). If consumers need to react to data exposed by the [State], they |
| 79 | + /// should subscribe to its streams or notifiers explicitly. |
| 80 | + static T? maybeOf<T extends State>(BuildContext context) { |
| 81 | + final element = |
| 82 | + context.getElementForInheritedWidgetOfExactType<StreamStateScope<T>>(); |
| 83 | + final scope = element?.widget as StreamStateScope<T>?; |
| 84 | + return scope?.state; |
| 85 | + } |
| 86 | + |
| 87 | + @override |
| 88 | + bool updateShouldNotify(StreamStateScope<T> oldWidget) => false; |
| 89 | +} |
0 commit comments