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';
10library;
11
12import 'package:flutter/foundation.dart';
13
14import '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.
65abstract 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
93class _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

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com