Documentation Updated
1 |
import 'package:flutter/widgets.dart'; |
|
2 |
import 'package:nested/nested.dart'; |
|
3 |
import 'package:provider/src/provider.dart'; |
|
4 |
import 'package:collection/collection.dart'; |
|
5 |
|
|
6 |
import 'consumer.dart'; |
|
7 |
|
|
8 |
typedef ShouldRebuild<T> = bool Function(T previous, T next); |
|
9 |
|
|
10 |
/// A base class for custom [Selector].
|
|
11 |
///
|
|
12 |
/// It works with any [InheritedWidget]. Variants like [Selector] and
|
|
13 |
/// [Selector6] are just syntax sugar to use [Selector0] with [Provider.of].
|
|
14 |
///
|
|
15 |
/// But it will **not** work with values
|
|
16 |
/// coming from anything but [InheritedWidget].
|
|
17 |
///
|
|
18 |
/// As such, the following:
|
|
19 |
///
|
|
20 |
/// ```dart
|
|
21 |
/// T value;
|
|
22 |
///
|
|
23 |
/// return Selector0(
|
|
24 |
/// selector: (_) => value,
|
|
25 |
/// builder: ...,
|
|
26 |
/// )
|
|
27 |
/// ```
|
|
28 |
///
|
|
29 |
/// will still call `builder` again, even if `value` didn't change.
|
|
30 |
class Selector0<T> extends SingleChildStatefulWidget { |
|
31 |
/// Both `builder` and `selector` must not be `null`.
|
|
32 | 3 |
Selector0({ |
33 |
Key key, |
|
34 |
@required this.builder, |
|
35 |
@required this.selector, |
|
36 |
ShouldRebuild<T> shouldRebuild, |
|
37 |
Widget child, |
|
38 | 3 |
}) : assert(builder != null), |
39 | 3 |
assert(selector != null), |
40 |
_shouldRebuild = shouldRebuild, |
|
41 | 3 |
super(key: key, child: child); |
42 |
|
|
43 |
/// A function that builds a widget tree from `child` and the last result of
|
|
44 |
/// [selector].
|
|
45 |
///
|
|
46 |
/// [builder] will be called again whenever the its parent widget asks for an
|
|
47 |
/// update, or if [selector] return a value that is different from the
|
|
48 |
/// previous one using [operator==].
|
|
49 |
///
|
|
50 |
/// Must not be `null`.
|
|
51 |
final ValueWidgetBuilder<T> builder; |
|
52 |
|
|
53 |
/// A function that obtains some [InheritedWidget] and map their content into
|
|
54 |
/// a new object with only a limited number of properties.
|
|
55 |
///
|
|
56 |
/// The returned object must implement [operator==].
|
|
57 |
///
|
|
58 |
/// Must not be `null`
|
|
59 |
final T Function(BuildContext) selector; |
|
60 |
|
|
61 |
final ShouldRebuild<T> _shouldRebuild; |
|
62 |
|
|
63 | 3 |
@override |
64 | 3 |
_Selector0State<T> createState() => _Selector0State<T>(); |
65 |
}
|
|
66 |
|
|
67 |
class _Selector0State<T> extends SingleChildState<Selector0<T>> { |
|
68 |
T value; |
|
69 |
Widget cache; |
|
70 |
Widget oldWidget; |
|
71 |
|
|
72 | 3 |
@override |
73 |
Widget buildWithChild(BuildContext context, Widget child) { |
|
74 | 3 |
final selected = widget.selector(context); |
75 |
|
|
76 | 3 |
var shouldInvalidateCache = oldWidget != widget || |
77 | 3 |
(widget._shouldRebuild != null && |
78 | 3 |
widget._shouldRebuild.call(value, selected)) || |
79 | 3 |
(widget._shouldRebuild == null && |
80 | 3 |
!const DeepCollectionEquality().equals(value, selected)); |
81 |
if (shouldInvalidateCache) { |
|
82 | 3 |
value = selected; |
83 | 3 |
oldWidget = widget; |
84 | 3 |
cache = widget.builder( |
85 |
context, |
|
86 |
selected, |
|
87 |
child, |
|
88 |
);
|
|
89 |
}
|
|
90 | 3 |
return cache; |
91 |
}
|
|
92 |
}
|
|
93 |
|
|
94 |
/// {@template provider.selector}
|
|
95 |
/// An equivalent to [Consumer] that can filter updates by selecting a limited
|
|
96 |
/// amount of values and prevent rebuild if they don't change.
|
|
97 |
///
|
|
98 |
/// [Selector] will obtain a value using [Provider.of], then pass that value
|
|
99 |
/// to `selector`. That `selector` callback is then tasked to return an object
|
|
100 |
/// that contains only the information needed for `builder` to complete.
|
|
101 |
///
|
|
102 |
/// By default, [Selector] determines if `builder` needs to be called again
|
|
103 |
/// by comparing the previous and new result of `selector` using
|
|
104 |
/// [DeepCollectionEquality] from the package `collection`.
|
|
105 |
///
|
|
106 |
/// This behavior can be overridden by passing a custom `shouldRebuild` callback.
|
|
107 |
///
|
|
108 |
/// **NOTE**:
|
|
109 |
/// The selected value must be immutable, or otherwise [Selector] may think
|
|
110 |
/// nothing changed and not call `builder` again.
|
|
111 |
///
|
|
112 |
/// As such, it `selector` should return either a collection ([List]/[Map]/[Set]/[Iterable])
|
|
113 |
/// or a class that override `==`.
|
|
114 |
///
|
|
115 |
/// To select multiple values without having to write a class that implements `==`,
|
|
116 |
/// the easiest solution is to use a "Tuple" from [tuple](https://pub.dev/packages/tuple):
|
|
117 |
///
|
|
118 |
/// ```dart
|
|
119 |
/// Selector<Foo, Tuple2<Bar, Baz>>(
|
|
120 |
/// selector: (_, foo) => Tuple2(foo.bar, foo.baz),
|
|
121 |
/// builder: (_, data, __) {
|
|
122 |
/// return Text('${data.item1} ${data.item2}');
|
|
123 |
/// }
|
|
124 |
/// )
|
|
125 |
/// ```
|
|
126 |
///
|
|
127 |
/// In that example, `builder` will be called again only if `foo.bar` or
|
|
128 |
/// `foo.baz` changes.
|
|
129 |
///
|
|
130 |
/// For generic usage information, see [Consumer].
|
|
131 |
/// {@endtemplate}
|
|
132 |
class Selector<A, S> extends Selector0<S> { |
|
133 |
/// {@macro provider.selector}
|
|
134 | 3 |
Selector({ |
135 |
Key key, |
|
136 |
@required ValueWidgetBuilder<S> builder, |
|
137 |
@required S Function(BuildContext, A) selector, |
|
138 |
ShouldRebuild<S> shouldRebuild, |
|
139 |
Widget child, |
|
140 | 3 |
}) : assert(selector != null), |
141 | 3 |
super( |
142 |
key: key, |
|
143 |
shouldRebuild: shouldRebuild, |
|
144 |
builder: builder, |
|
145 | 3 |
selector: (context) => selector(context, Provider.of(context)), |
146 |
child: child, |
|
147 |
);
|
|
148 |
}
|
|
149 |
|
|
150 |
/// {@macro provider.selector}
|
|
151 |
class Selector2<A, B, S> extends Selector0<S> { |
|
152 |
/// {@macro provider.selector}
|
|
153 | 3 |
Selector2({ |
154 |
Key key, |
|
155 |
@required ValueWidgetBuilder<S> builder, |
|
156 |
@required S Function(BuildContext, A, B) selector, |
|
157 |
ShouldRebuild<S> shouldRebuild, |
|
158 |
Widget child, |
|
159 | 3 |
}) : assert(selector != null), |
160 | 3 |
super( |
161 |
key: key, |
|
162 |
shouldRebuild: shouldRebuild, |
|
163 |
builder: builder, |
|
164 | 3 |
selector: (context) => selector( |
165 |
context, |
|
166 | 3 |
Provider.of(context), |
167 | 3 |
Provider.of(context), |
168 |
),
|
|
169 |
child: child, |
|
170 |
);
|
|
171 |
}
|
|
172 |
|
|
173 |
/// {@macro provider.selector}
|
|
174 |
class Selector3<A, B, C, S> extends Selector0<S> { |
|
175 |
/// {@macro provider.selector}
|
|
176 | 3 |
Selector3({ |
177 |
Key key, |
|
178 |
@required ValueWidgetBuilder<S> builder, |
|
179 |
@required S Function(BuildContext, A, B, C) selector, |
|
180 |
ShouldRebuild<S> shouldRebuild, |
|
181 |
Widget child, |
|
182 | 3 |
}) : assert(selector != null), |
183 | 3 |
super( |
184 |
key: key, |
|
185 |
shouldRebuild: shouldRebuild, |
|
186 |
builder: builder, |
|
187 | 3 |
selector: (context) => selector( |
188 |
context, |
|
189 | 3 |
Provider.of(context), |
190 | 3 |
Provider.of(context), |
191 | 3 |
Provider.of(context), |
192 |
),
|
|
193 |
child: child, |
|
194 |
);
|
|
195 |
}
|
|
196 |
|
|
197 |
/// {@macro provider.selector}
|
|
198 |
class Selector4<A, B, C, D, S> extends Selector0<S> { |
|
199 |
/// {@macro provider.selector}
|
|
200 | 3 |
Selector4({ |
201 |
Key key, |
|
202 |
@required ValueWidgetBuilder<S> builder, |
|
203 |
@required S Function(BuildContext, A, B, C, D) selector, |
|
204 |
ShouldRebuild<S> shouldRebuild, |
|
205 |
Widget child, |
|
206 | 3 |
}) : assert(selector != null), |
207 | 3 |
super( |
208 |
key: key, |
|
209 |
shouldRebuild: shouldRebuild, |
|
210 |
builder: builder, |
|
211 | 3 |
selector: (context) => selector( |
212 |
context, |
|
213 | 3 |
Provider.of(context), |
214 | 3 |
Provider.of(context), |
215 | 3 |
Provider.of(context), |
216 | 3 |
Provider.of(context), |
217 |
),
|
|
218 |
child: child, |
|
219 |
);
|
|
220 |
}
|
|
221 |
|
|
222 |
/// {@macro provider.selector}
|
|
223 |
class Selector5<A, B, C, D, E, S> extends Selector0<S> { |
|
224 |
/// {@macro provider.selector}
|
|
225 | 3 |
Selector5({ |
226 |
Key key, |
|
227 |
@required ValueWidgetBuilder<S> builder, |
|
228 |
@required S Function(BuildContext, A, B, C, D, E) selector, |
|
229 |
ShouldRebuild<S> shouldRebuild, |
|
230 |
Widget child, |
|
231 | 3 |
}) : assert(selector != null), |
232 | 3 |
super( |
233 |
key: key, |
|
234 |
shouldRebuild: shouldRebuild, |
|
235 |
builder: builder, |
|
236 | 3 |
selector: (context) => selector( |
237 |
context, |
|
238 | 3 |
Provider.of(context), |
239 | 3 |
Provider.of(context), |
240 | 3 |
Provider.of(context), |
241 | 3 |
Provider.of(context), |
242 | 3 |
Provider.of(context), |
243 |
),
|
|
244 |
child: child, |
|
245 |
);
|
|
246 |
}
|
|
247 |
|
|
248 |
/// {@macro provider.selector}
|
|
249 |
class Selector6<A, B, C, D, E, F, S> extends Selector0<S> { |
|
250 |
/// {@macro provider.selector}
|
|
251 | 3 |
Selector6({ |
252 |
Key key, |
|
253 |
@required ValueWidgetBuilder<S> builder, |
|
254 |
@required S Function(BuildContext, A, B, C, D, E, F) selector, |
|
255 |
ShouldRebuild<S> shouldRebuild, |
|
256 |
Widget child, |
|
257 | 3 |
}) : assert(selector != null), |
258 | 3 |
super( |
259 |
key: key, |
|
260 |
shouldRebuild: shouldRebuild, |
|
261 |
builder: builder, |
|
262 | 3 |
selector: (context) => selector( |
263 |
context, |
|
264 | 3 |
Provider.of(context), |
265 | 3 |
Provider.of(context), |
266 | 3 |
Provider.of(context), |
267 | 3 |
Provider.of(context), |
268 | 3 |
Provider.of(context), |
269 | 3 |
Provider.of(context), |
270 |
),
|
|
271 |
child: child, |
|
272 |
);
|
|
273 |
}
|
Read our documentation on viewing source code .