| 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | /// @docImport 'package:flutter/animation.dart'; |
| 6 | /// @docImport 'package:flutter/rendering.dart'; |
| 7 | /// |
| 8 | /// @docImport 'inherited_model.dart'; |
| 9 | /// @docImport 'scroll_position.dart'; |
| 10 | library; |
| 11 | |
| 12 | import 'package:flutter/foundation.dart'; |
| 13 | |
| 14 | import 'framework.dart'; |
| 15 | |
| 16 | /// An inherited widget for a [Listenable] [notifier], which updates its |
| 17 | /// dependencies when the [notifier] is triggered. |
| 18 | /// |
| 19 | /// This is a variant of [InheritedWidget], specialized for subclasses of |
| 20 | /// [Listenable], such as [ChangeNotifier] or [ValueNotifier]. |
| 21 | /// |
| 22 | /// Dependents are notified whenever the [notifier] sends notifications, or |
| 23 | /// whenever the identity of the [notifier] changes. |
| 24 | /// |
| 25 | /// Multiple notifications are coalesced, so that dependents only rebuild once |
| 26 | /// even if the [notifier] fires multiple times between two frames. |
| 27 | /// |
| 28 | /// Typically this class is subclassed with a class that provides an `of` static |
| 29 | /// method that calls [BuildContext.dependOnInheritedWidgetOfExactType] with that |
| 30 | /// class. |
| 31 | /// |
| 32 | /// The [updateShouldNotify] method may also be overridden, to change the logic |
| 33 | /// in the cases where [notifier] itself is changed. The [updateShouldNotify] |
| 34 | /// method is called with the old [notifier] in the case of the [notifier] being |
| 35 | /// changed. When it returns true, the dependents are marked as needing to be |
| 36 | /// rebuilt this frame. |
| 37 | /// |
| 38 | /// {@tool dartpad} |
| 39 | /// This example shows three spinning squares that use the value of the notifier |
| 40 | /// on an ancestor [InheritedNotifier] (`SpinModel`) to give them their |
| 41 | /// rotation. The [InheritedNotifier] doesn't need to know about the children, |
| 42 | /// and the `notifier` argument doesn't need to be an animation controller, it |
| 43 | /// can be anything that implements [Listenable] (like a [ChangeNotifier]). |
| 44 | /// |
| 45 | /// The `SpinModel` class could just as easily listen to another object (say, a |
| 46 | /// separate object that keeps the value of an input or data model value) that |
| 47 | /// is a [Listenable], and get the value from that. The descendants also don't |
| 48 | /// need to have an instance of the [InheritedNotifier] in order to use it, they |
| 49 | /// just need to know that there is one in their ancestry. This can help with |
| 50 | /// decoupling widgets from their models. |
| 51 | /// |
| 52 | /// ** See code in examples/api/lib/widgets/inherited_notifier/inherited_notifier.0.dart ** |
| 53 | /// {@end-tool} |
| 54 | /// |
| 55 | /// See also: |
| 56 | /// |
| 57 | /// * [Animation], an implementation of [Listenable] that ticks each frame to |
| 58 | /// update a value. |
| 59 | /// * [ViewportOffset] or its subclass [ScrollPosition], implementations of |
| 60 | /// [Listenable] that trigger when a view is scrolled. |
| 61 | /// * [InheritedWidget], an inherited widget that only notifies dependents |
| 62 | /// when its value is different. |
| 63 | /// * [InheritedModel], an inherited widget that allows clients to subscribe |
| 64 | /// to changes for subparts of the value. |
| 65 | abstract class InheritedNotifier<T extends Listenable> extends InheritedWidget { |
| 66 | /// Create an inherited widget that updates its dependents when [notifier] |
| 67 | /// sends notifications. |
| 68 | const InheritedNotifier({super.key, this.notifier, required super.child}); |
| 69 | |
| 70 | /// The [Listenable] object to which to listen. |
| 71 | /// |
| 72 | /// Whenever this object sends change notifications, the dependents of this |
| 73 | /// widget are triggered. |
| 74 | /// |
| 75 | /// By default, whenever the [notifier] is changed (including when changing to |
| 76 | /// or from null), if the old notifier is not equal to the new notifier (as |
| 77 | /// determined by the `==` operator), notifications are sent. This behavior |
| 78 | /// can be overridden by overriding [updateShouldNotify]. |
| 79 | /// |
| 80 | /// While the [notifier] is null, no notifications are sent, since the null |
| 81 | /// object cannot itself send notifications. |
| 82 | final T? notifier; |
| 83 | |
| 84 | @override |
| 85 | bool updateShouldNotify(InheritedNotifier<T> oldWidget) { |
| 86 | return oldWidget.notifier != notifier; |
| 87 | } |
| 88 | |
| 89 | @override |
| 90 | InheritedElement createElement() => _InheritedNotifierElement<T>(this); |
| 91 | } |
| 92 | |
| 93 | class _InheritedNotifierElement<T extends Listenable> extends InheritedElement { |
| 94 | _InheritedNotifierElement(InheritedNotifier<T> widget) : super(widget) { |
| 95 | widget.notifier?.addListener(_handleUpdate); |
| 96 | } |
| 97 | |
| 98 | bool _dirty = false; |
| 99 | |
| 100 | @override |
| 101 | void update(InheritedNotifier<T> newWidget) { |
| 102 | final T? oldNotifier = (widget as InheritedNotifier<T>).notifier; |
| 103 | final T? newNotifier = newWidget.notifier; |
| 104 | if (oldNotifier != newNotifier) { |
| 105 | oldNotifier?.removeListener(_handleUpdate); |
| 106 | newNotifier?.addListener(_handleUpdate); |
| 107 | } |
| 108 | super.update(newWidget); |
| 109 | } |
| 110 | |
| 111 | @override |
| 112 | Widget build() { |
| 113 | if (_dirty) { |
| 114 | notifyClients(widget as InheritedNotifier<T>); |
| 115 | } |
| 116 | return super.build(); |
| 117 | } |
| 118 | |
| 119 | void _handleUpdate() { |
| 120 | _dirty = true; |
| 121 | markNeedsBuild(); |
| 122 | } |
| 123 | |
| 124 | @override |
| 125 | void notifyClients(InheritedNotifier<T> oldWidget) { |
| 126 | super.notifyClients(oldWidget); |
| 127 | _dirty = false; |
| 128 | } |
| 129 | |
| 130 | @override |
| 131 | void unmount() { |
| 132 | (widget as InheritedNotifier<T>).notifier?.removeListener(_handleUpdate); |
| 133 | super.unmount(); |
| 134 | } |
| 135 | } |
| 136 | |