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 .

Loading