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 'app.dart';
6library;
7
8import 'framework.dart';
9import 'inherited_model.dart';
10
11/// The type of the [SharedAppData.getValue] `init` parameter.
12///
13/// This callback is used to lazily create the initial value for
14/// a [SharedAppData] keyword.
15typedef SharedAppDataInitCallback<T> = T Function();
16
17/// Enables sharing key/value data with its `child` and all of the
18/// child's descendants.
19///
20/// - `SharedAppData.getValue(context, key, initCallback)` creates a dependency
21/// on the key and returns the value for the key from the shared data table.
22/// If no value exists for key then the initCallback is used to create
23/// the initial value.
24///
25/// - `SharedAppData.setValue(context, key, value)` changes the value of an entry
26/// in the shared data table and forces widgets that depend on that entry
27/// to be rebuilt.
28///
29/// A widget whose build method uses SharedAppData.getValue(context,
30/// keyword, initCallback) creates a dependency on the SharedAppData. When
31/// the value of keyword changes with SharedAppData.setValue(), the widget
32/// will be rebuilt. The values managed by the SharedAppData are expected
33/// to be immutable: intrinsic changes to values will not cause
34/// dependent widgets to be rebuilt.
35///
36/// An instance of this widget is created automatically by [WidgetsApp].
37///
38/// There are many ways to share data with a widget subtree. This
39/// class is based on [InheritedModel], which is an [InheritedWidget].
40/// It's intended to be used by packages that need to share a modest
41/// number of values among their own components.
42///
43/// SharedAppData is not intended to be a substitute for Provider or any of
44/// the other general purpose application state systems. SharedAppData is
45/// for situations where a package's custom widgets need to share one
46/// or a handful of immutable data objects that can be lazily
47/// initialized. It exists so that packages like that can deliver
48/// custom widgets without requiring the developer to add a
49/// package-specific umbrella widget to their application.
50///
51/// A good way to create an SharedAppData key that avoids potential
52/// collisions with other packages is to use a static `Object()` value.
53/// The `SharedObject` example below does this.
54///
55/// {@tool dartpad}
56/// The following sample demonstrates using the automatically created
57/// [SharedAppData]. Button presses cause changes to the values for keys
58/// 'foo', and 'bar', and those changes only cause the widgets that
59/// depend on those keys to be rebuilt.
60///
61/// ** See code in examples/api/lib/widgets/shared_app_data/shared_app_data.0.dart **
62/// {@end-tool}
63///
64/// {@tool dartpad}
65/// The following sample demonstrates how a single lazily computed
66/// value could be shared within an app. A Flutter package that
67/// provided custom widgets might use this approach to share a (possibly
68/// private) value with instances of those widgets.
69///
70/// ** See code in examples/api/lib/widgets/shared_app_data/shared_app_data.1.dart **
71/// {@end-tool}
72class SharedAppData extends StatefulWidget {
73 /// Creates a widget based on [InheritedModel] that supports build
74 /// dependencies qualified by keywords. Descendant widgets create
75 /// such dependencies with [SharedAppData.getValue] and they trigger
76 /// rebuilds with [SharedAppData.setValue].
77 ///
78 /// This widget is automatically created by the [WidgetsApp].
79 const SharedAppData({super.key, required this.child});
80
81 /// The widget below this widget in the tree.
82 ///
83 /// {@macro flutter.widgets.ProxyWidget.child}
84 final Widget child;
85
86 @override
87 State<StatefulWidget> createState() => _SharedAppDataState();
88
89 /// Returns the app model's value for `key` and ensures that each
90 /// time the value of `key` is changed with [SharedAppData.setValue], the
91 /// specified context will be rebuilt.
92 ///
93 /// If no value for `key` exists then the `init` callback is used to
94 /// generate an initial value. The callback is expected to return
95 /// an immutable value because intrinsic changes to the value will
96 /// not cause dependent widgets to be rebuilt.
97 ///
98 /// A widget that depends on the app model's value for `key` should use
99 /// this method in their `build` methods to ensure that they are rebuilt
100 /// if the value changes.
101 ///
102 /// The type parameter `K` is the type of the keyword and `V`
103 /// is the type of the value.
104 static V getValue<K extends Object, V>(
105 BuildContext context,
106 K key,
107 SharedAppDataInitCallback<V> init,
108 ) {
109 final _SharedAppModel? model = InheritedModel.inheritFrom<_SharedAppModel>(
110 context,
111 aspect: key,
112 );
113 assert(_debugHasSharedAppData(model, context, 'getValue'));
114 return model!.sharedAppDataState.getValue<K, V>(key, init);
115 }
116
117 /// Changes the app model's `value` for `key` and rebuilds any widgets
118 /// that have created a dependency on `key` with [SharedAppData.getValue].
119 ///
120 /// If `value` is `==` to the current value of `key` then nothing
121 /// is rebuilt.
122 ///
123 /// The `value` is expected to be immutable because intrinsic
124 /// changes to the value will not cause dependent widgets to be
125 /// rebuilt.
126 ///
127 /// Unlike [SharedAppData.getValue], this method does _not_ create a dependency
128 /// between `context` and `key`.
129 ///
130 /// The type parameter `K` is the type of the value's keyword and `V`
131 /// is the type of the value.
132 static void setValue<K extends Object, V>(BuildContext context, K key, V value) {
133 final _SharedAppModel? model = context.getInheritedWidgetOfExactType<_SharedAppModel>();
134 assert(_debugHasSharedAppData(model, context, 'setValue'));
135 model!.sharedAppDataState.setValue<K, V>(key, value);
136 }
137
138 static bool _debugHasSharedAppData(
139 _SharedAppModel? model,
140 BuildContext context,
141 String methodName,
142 ) {
143 assert(() {
144 if (model == null) {
145 throw FlutterError.fromParts(<DiagnosticsNode>[
146 ErrorSummary('No SharedAppData widget found.'),
147 ErrorDescription(
148 'SharedAppData.$methodName requires an SharedAppData widget ancestor.\n',
149 ),
150 context.describeWidget(
151 'The specific widget that could not find an SharedAppData ancestor was',
152 ),
153 context.describeOwnershipChain('The ownership chain for the affected widget is'),
154 ErrorHint(
155 'Typically, the SharedAppData widget is introduced by the MaterialApp '
156 'or WidgetsApp widget at the top of your application widget tree. It '
157 'provides a key/value map of data that is shared with the entire '
158 'application.',
159 ),
160 ]);
161 }
162 return true;
163 }());
164 return true;
165 }
166}
167
168class _SharedAppDataState extends State<SharedAppData> {
169 late Map<Object, Object?> data = <Object, Object?>{};
170
171 @override
172 Widget build(BuildContext context) {
173 return _SharedAppModel(sharedAppDataState: this, child: widget.child);
174 }
175
176 V getValue<K extends Object, V>(K key, SharedAppDataInitCallback<V> init) {
177 data[key] ??= init();
178 return data[key] as V;
179 }
180
181 void setValue<K extends Object, V>(K key, V value) {
182 if (data[key] != value) {
183 setState(() {
184 data = Map<Object, Object?>.of(data);
185 data[key] = value;
186 });
187 }
188 }
189}
190
191class _SharedAppModel extends InheritedModel<Object> {
192 _SharedAppModel({required this.sharedAppDataState, required super.child})
193 : data = sharedAppDataState.data;
194
195 final _SharedAppDataState sharedAppDataState;
196 final Map<Object, Object?> data;
197
198 @override
199 bool updateShouldNotify(_SharedAppModel old) {
200 return data != old.data;
201 }
202
203 @override
204 bool updateShouldNotifyDependent(_SharedAppModel old, Set<Object> keys) {
205 for (final Object key in keys) {
206 if (data[key] != old.data[key]) {
207 return true;
208 }
209 }
210 return false;
211 }
212}
213

Provided by KDAB

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