Line data Source code
1 : import 'dart:collection';
2 : import 'dart:math';
3 :
4 : import 'package:flutter/foundation.dart';
5 : import 'package:collection_providers/collection_providers.dart';
6 :
7 : /// An implementation of [ChangeNotifier] that allows implementers to interact
8 : /// with this provider as if it were a [Map] and be notified when any changes
9 : /// to the map have been made
10 : ///
11 : /// Like [ChangeNotifier], this is optimized for small numbers of listeners.
12 : /// It is O(N) for adding and removing listeners.
13 : ///
14 : /// [K] is the type of the Key to be used by this map. It is usually a [String],
15 : /// but can be any subclass of [Object]. It must implement `operator==` and
16 : /// `hashCode`
17 : ///
18 : /// [T] is the type of the Value to be used by this map. It can be any subclass
19 : /// of [Object] and has no special requirements.
20 : class ListChangeNotifier<T> extends CollectionChangeNotifier with ListMixin<T> {
21 : List<T> _list;
22 :
23 2 : ListChangeNotifier([List<T> backingList]) {
24 6 : _list = List<T>.from(backingList ?? []);
25 : }
26 :
27 : /// The number of objects in this list.
28 : ///
29 : /// The valid indices for a list are `0` through `length - 1`
30 1 : @override
31 : int get length {
32 1 : assert(_debugAssertNotDisposed());
33 2 : return _list.length;
34 : }
35 :
36 : /// Changes the length of this list.
37 : /// Does not notify listeners.
38 : ///
39 : /// If [newLength] is greater than the current length, entries are initialized to `null`.
40 1 : @override
41 : set length(int newLength) {
42 1 : assert(_debugAssertNotDisposed());
43 2 : _list.length = newLength;
44 : }
45 :
46 : /// Returns the object at the given [index] in the list
47 : /// or throws a [RangeError] if [index] is out of bounds.
48 1 : @override
49 : T operator [](int index) {
50 1 : assert(_debugAssertNotDisposed());
51 2 : return _list[index];
52 : }
53 :
54 : /// Sets the value at the given [index] in the list to [value]
55 : /// or throws a [RangeError] if [index] is out of bounds.
56 1 : @override
57 : void operator []=(int index, T value) {
58 1 : assert(_debugAssertNotDisposed());
59 2 : _list[index] = value;
60 1 : notifyListeners();
61 : }
62 :
63 : /// Clear all objects from the list.
64 : /// Listeners are notified after the list is cleared.
65 1 : @override
66 : void clear() {
67 1 : assert(_debugAssertNotDisposed());
68 1 : super.clear();
69 1 : notifyListeners();
70 : }
71 :
72 : /// Appends all objects of [iterable] to the end of the list.
73 : /// Listeners are notified after all objects have been added.
74 : ///
75 : /// Extends the length of the list by the number of objects in [iterable].
76 1 : @override
77 : void addAll(Iterable<T> iterable) {
78 1 : assert(_debugAssertNotDisposed());
79 1 : assert(iterable != null);
80 2 : _list.addAll(iterable);
81 1 : notifyListeners();
82 : }
83 :
84 : /// Removes all objects from this list that satisfy [test].
85 : /// Listeners are notified after all objects have been removed.
86 : ///
87 : /// An object [:o:] satisfies [test] if [:test(o):] is true.
88 1 : @override
89 : void removeWhere(bool Function(T element) test) {
90 1 : assert(_debugAssertNotDisposed());
91 1 : assert(test != null);
92 2 : _list.removeWhere(test);
93 1 : notifyListeners();
94 : }
95 :
96 : /// Removes all objects from the list that fail to satisfy [test].
97 : /// Listeners are notified after all objects have been removed.
98 : ///
99 : /// An object [:o:] satisfies [test] if [:test(o):] is true.
100 1 : @override
101 : void retainWhere(bool Function(T element) test) {
102 1 : assert(_debugAssertNotDisposed());
103 1 : assert(test != null);
104 2 : _list.retainWhere(test);
105 1 : notifyListeners();
106 : }
107 :
108 : /// Sorts this list according to the order specified by the [compare] function.
109 : /// Listeners are notified after sorting is completed.
110 : ///
111 : /// The [compare] function must act as a [Comparator].
112 : /// The default List implementations use [Comparable.compare] if [compare] is omitted.
113 1 : @override
114 : void sort([int Function(T a, T b) compare]) {
115 1 : assert(_debugAssertNotDisposed());
116 2 : _list.sort(compare);
117 1 : notifyListeners();
118 : }
119 :
120 : /// Shuffles the elements of this list randomly.
121 : /// Listeners are notified after shuffling is completed.
122 1 : @override
123 : void shuffle([Random random]) {
124 1 : assert(_debugAssertNotDisposed());
125 2 : _list.shuffle(random);
126 1 : notifyListeners();
127 : }
128 :
129 : /// Removes the objects in the range [start] inclusive to [end] exclusive.
130 : /// Listeners are notified after all objects are removed.
131 : ///
132 : /// The provided range, given by [start] to [end], must be valid.
133 : /// A range from [start] to [end] is valid if `0 <= start <= end <= len`, where
134 : /// `len` ist he list's `length`. The range starts at `start` and has length
135 : /// `end - start`. An empty range (with `end == start`) is valid.
136 1 : @override
137 : void removeRange(int start, int end) {
138 1 : assert(_debugAssertNotDisposed());
139 2 : _list.removeRange(start, end);
140 1 : notifyListeners();
141 : }
142 :
143 : /// Sets the objects in the range [start] inclusive to [end] exclusive to the
144 : /// given [fill]
145 : /// Listeners are notified after all objects are updated.
146 : ///
147 : /// The provided range, given by [start] to [end], must be valid.
148 : /// A range from [start] to [end] is valid if `0 <= start <= end <= len`, where
149 : /// `len` ist he list's `length`. The range starts at `start` and has length
150 : /// `end - start`. An empty range (with `end == start`) is valid.
151 1 : @override
152 : void fillRange(int start, int end, [T fill]) {
153 1 : assert(_debugAssertNotDisposed());
154 2 : _list.fillRange(start, end, fill);
155 1 : notifyListeners();
156 : }
157 :
158 : /// Copies the objects of [iterable], skipping [skipCount] object first, into
159 : /// the range [start] inclusive to [end] exclusive of the list.
160 : /// Listeners are notified after all the values have been set.
161 : ///
162 : /// The provided range, given by [start] to [end], must be valid.
163 : /// A range from [start] to [end] is valid if `0 <= start <= end <= len`, where
164 : /// `len` ist he list's `length`. The range starts at `start` and has length
165 : /// `end - start`. An empty range (with `end == start`) is valid.
166 : ///
167 : /// The [iterable] must have enough objects to fill the range from [start] to
168 : /// [end] after skipping [skipCount] objects.
169 : ///
170 : /// If [iterable] is this list, the operation copies the elements originally
171 : /// in the range from `skipCount` to `skipCount + (end - start)` to the range
172 : /// `start` to `end`, even if the two ranges overlap.
173 : ///
174 : /// If [iterable] depends on this list in some other way, no guarantees are made.
175 1 : @override
176 : void setRange(int start, int end, Iterable<T> iterable, [int skipCount = 0]) {
177 1 : assert(_debugAssertNotDisposed());
178 1 : assert(iterable != null);
179 2 : _list.setRange(start, end, iterable, skipCount);
180 1 : notifyListeners();
181 : }
182 :
183 : /// Removes the objects in the range [start] inclusive to [end] exclusive and
184 : /// inserts the contents of [newContents] in its place.
185 : /// Listeners are notified after all the objects have been replaced.
186 : ///
187 : /// The provided range, given by [start] to [end], must be valid.
188 : /// A range from [start] to [end] is valid if `0 <= start <= end <= len`, where
189 : /// `len` is the list's `length`. The range starts at `start` and has length
190 : /// `end - start`. An empty range (with `end == start`) is valid.
191 1 : @override
192 : void replaceRange(int start, int end, Iterable<T> newContents) {
193 1 : assert(_debugAssertNotDisposed());
194 1 : assert(newContents != null);
195 2 : _list.replaceRange(start, end, newContents);
196 1 : notifyListeners();
197 : }
198 :
199 : /// Insert [element] at the position [index] in this list.
200 : /// Listeners are notified after all objects have been repositioned.
201 : ///
202 : /// This increases the length of the list by one and shifts all objects at or
203 : /// after the index towards the end of the list.
204 : ///
205 : /// The [index] value must be non-negative and no greater than [length].
206 1 : @override
207 : void insert(int index, T element) {
208 1 : assert(_debugAssertNotDisposed());
209 2 : _list.insert(index, element);
210 1 : notifyListeners();
211 : }
212 :
213 : /// Removes the object at position [index] from the list.
214 : /// Listeners are notified after all objects have been repositioned.
215 : ///
216 : /// This method reduces the length of `this` by one and moves all later objects
217 : /// down by one position.
218 : ///
219 : /// Returns the removed object.
220 : ///
221 : /// The [index] must be in the range `0 ≤ index < length`.
222 1 : @override
223 : T removeAt(int index) {
224 1 : assert(_debugAssertNotDisposed());
225 2 : final result = _list.removeAt(index);
226 1 : notifyListeners();
227 : return result;
228 : }
229 :
230 : /// Inserts all objects of [iterable] at position [index] in this list.
231 : /// Listeners are notified after all objects have been inserted.
232 : ///
233 : /// This increases the length of the list by the length of [iterable] and
234 : /// shifts all later objects towards the end of the list.
235 : ///
236 : /// The [index] value must be non-negative and no greater than [length].
237 1 : @override
238 : void insertAll(int index, Iterable<T> iterable) {
239 1 : assert(_debugAssertNotDisposed());
240 1 : assert(iterable != null);
241 2 : _list.insertAll(index, iterable);
242 1 : notifyListeners();
243 : }
244 :
245 : /// Overwrites objects of `this` with the objects of [iterable] start at
246 : /// positions [index] in this list.
247 : /// Listeners are notified after all objects have been set.
248 : ///
249 : /// This operation does not icrease the length of `this`.
250 : ///
251 : /// The [index] must be non-negative and no greater than [length].
252 : ///
253 : /// The [iterable] must not have more elements than what can fit from [index]
254 : /// to [length].
255 : ///
256 : /// if `iterable` is based on this list, its values may change /during/ the
257 : /// `setAll` operation.
258 1 : @override
259 : void setAll(int index, Iterable<T> iterable) {
260 1 : assert(_debugAssertNotDisposed());
261 1 : assert(iterable != null);
262 2 : _list.setAll(index, iterable);
263 1 : notifyListeners();
264 : }
265 :
266 : /// Discards the internal resources used by the object.
267 : /// After this is called, the object is not in a usable state and should be discarded.
268 : ///
269 : /// This method should only be called by the object's owner.
270 2 : @override
271 : @mustCallSuper
272 : void dispose() {
273 2 : assert(_debugAssertNotDisposed());
274 2 : _list = null;
275 2 : super.dispose();
276 : }
277 :
278 : /// Reimplemented from [ChangeNotifier]
279 2 : bool _debugAssertNotDisposed() {
280 2 : assert(() {
281 2 : if (_list == null) {
282 3 : throw FlutterError('A $runtimeType was used after being disposed\n'
283 1 : 'Once you have called dispose() on a $runtimeType it can no longer by used.');
284 : }
285 : return true;
286 2 : }());
287 : return true;
288 : }
289 : }
|