Line data Source code
1 : import 'dart:collection';
2 :
3 : import 'package:flutter/foundation.dart';
4 : import 'package:collection_providers/collection_providers.dart';
5 :
6 : /// An implementation of [ChangeNotifier] that allows implementers to interact
7 : /// with this provider as if it were a [Map] and be notified when any changes
8 : /// to the map have been made
9 : ///
10 : /// Like [ChangeNotifier], this is optimized for small numbers of listeners.
11 : /// It is O(N) for adding and removing listeners.
12 : ///
13 : /// [K] is the type of the Key to be used by this map. It is usually a [String],
14 : /// but can be any subclass of [Object]. It must implement `operator==` and
15 : /// `hashCode`
16 : ///
17 : /// [T] is the type of the Value to be used by this map. It can be any subclass
18 : /// of [Object] and has no special requirements.
19 : class MapChangeNotifier<K, T> extends CollectionChangeNotifier
20 : with MapMixin<K, T> {
21 : Map<K, T> _map;
22 :
23 2 : MapChangeNotifier([Map<K, T> backingMap]) {
24 6 : _map = Map<K, T>.from(backingMap ?? {});
25 : }
26 :
27 : /// Returns the value for the given [key] or null if [key] is not in the map.
28 1 : @override
29 : T operator [](Object key) {
30 1 : assert(_debugAssertNotDisposed());
31 2 : return _map[key];
32 : }
33 :
34 : /// Associates the [key] with the given [value].
35 : ///
36 : /// If the key was already in the map, its associated value is changed.
37 : /// Otherwise the key/value pair is added to the map.
38 1 : @override
39 : void operator []=(K key, T value) {
40 1 : assert(_debugAssertNotDisposed());
41 2 : _map[key] = value;
42 1 : notifyListeners();
43 : }
44 :
45 : /// Removes all pairs from the map.
46 : ///
47 : /// After this, the map is empty.
48 1 : @override
49 : void clear() {
50 1 : assert(_debugAssertNotDisposed());
51 2 : _map.clear();
52 1 : notifyListeners();
53 : }
54 :
55 : /// The keys of [this].
56 : ///
57 : /// The returned iterable has efficient `length` and `contains` operations
58 : /// based on [length] and [containsKey] of the map.
59 : ///
60 : /// Modifying the map while iterating the keys may break the iteration.
61 1 : @override
62 : Iterable<K> get keys {
63 1 : assert(_debugAssertNotDisposed());
64 2 : return _map.keys;
65 : }
66 :
67 : /// Removes [key] and its associated value, if present, from the map.
68 : ///
69 : /// Returns the value associated with [key] before it was removed.
70 : /// Returns `null` if [key] was not in the map.
71 : ///
72 : /// Note that values can be `null` and a returned `null` value doesn't
73 : /// always mean that the key was absent.
74 1 : @override
75 : T remove(Object key) {
76 1 : assert(_debugAssertNotDisposed());
77 2 : var removed = _map.remove(key);
78 1 : notifyListeners();
79 : return removed;
80 : }
81 :
82 : /// Adds all the key/value pairs of [other] to this map.
83 : /// Listeners are notified after all values have been added.
84 : ///
85 : /// If a key of [other] is already in this map, its value is overwritten.
86 : ///
87 : /// The operation is equivalent to doing `this[key] = value` for each key
88 : /// and associated value in [other]. It iterates over [other], which must
89 : /// therefore not change during iteration.
90 1 : @override
91 : void addAll(Map<K, T> other) {
92 1 : assert(_debugAssertNotDisposed());
93 1 : assert(other != null);
94 2 : _map.addAll(other);
95 1 : notifyListeners();
96 : }
97 :
98 : /// Updates all values.
99 : /// Listeners are notified after all values have been updated.
100 : ///
101 : /// Iterates over all entries in the map and updates them with the result of invoking [update].
102 1 : @override
103 : void updateAll(T Function(K key, T value) update) {
104 1 : assert(_debugAssertNotDisposed());
105 1 : assert(update != null);
106 2 : _map.updateAll(update);
107 1 : notifyListeners();
108 : }
109 :
110 : /// Adds all the key/value pairs of [newEntries] to othis map.
111 : /// Listeners are notified after all the entries have been added.
112 : ///
113 : /// If a key of [newEntries] is already in the map,
114 : /// the corresponding value is ovewritten.
115 : ///
116 : /// The operations is equivalent to doing `this[entry.key] = entry.value`
117 : /// for each [MapEntry] of the iterable.
118 1 : @override
119 : void addEntries(Iterable<MapEntry<K, T>> newEntries) {
120 1 : assert(_debugAssertNotDisposed());
121 1 : assert(newEntries != null);
122 2 : _map.addEntries(newEntries);
123 1 : notifyListeners();
124 : }
125 :
126 : /// Remove all entries of this map that satisfy [test]
127 : /// Listeners are notified after all removals have taken place.
128 : ///
129 : /// An entry [:e:] satisfies [test] if [:test(e.key, e.value):] is true.
130 1 : @override
131 : void removeWhere(bool Function(K key, T value) test) {
132 1 : assert(_debugAssertNotDisposed());
133 1 : assert(test != null);
134 2 : _map.removeWhere(test);
135 1 : notifyListeners();
136 : }
137 :
138 : /// Discards the internal resources used by the object.
139 : /// After this is called, the object is not in a usable state and should be discarded.
140 : ///
141 : /// This method should only be called by the object's owner.
142 2 : @override
143 : @mustCallSuper
144 : void dispose() {
145 2 : assert(_debugAssertNotDisposed());
146 2 : _map = null;
147 2 : super.dispose();
148 : }
149 :
150 : /// Reimplemented from [ChangeNotifier]
151 2 : bool _debugAssertNotDisposed() {
152 2 : assert(() {
153 2 : if (_map == null) {
154 3 : throw FlutterError('A $runtimeType was used after being disposed\n'
155 1 : 'Once you have called dispose() on a $runtimeType it can no longer by used.');
156 : }
157 : return true;
158 2 : }());
159 : return true;
160 : }
161 : }
|