Repository: mobxjs/mobx.dart

Issue:
`ObservableSet` and `ObservableMap` notify all listeners when one is added with `fireImmediately: true`
When a new listener is added to either `ObservableSet` or `ObservableMap` by calling `.observe` with `fireImmediately: true`, it will also fire all previously added listeners.

Expected behavior is that only the listener being added is fired immediately.

(This is not the case for `ObservableList` however which does have the expected behavior.)

See the following example code:

```dart
import 'package:mobx/mobx.dart';

void main() {
  print('>>> ObservableSet');
  final exampleSet = ObservableSet.of([1, 2, 3]);
  exampleSet.observe((_) => print('First set listener should never fire'));
  exampleSet.observe((_) => print('Second set listener'), fireImmediately: true);

  print('\n>>> ObservableMap');
  final exampleMap = ObservableMap.of({'a': 1, 'b': 2, 'c': 3});
  exampleMap.observe((_) => print('First map listener should never fire'));
  exampleMap.observe((_) => print('Second map listener'), fireImmediately: true);

  print('\n>>> ObservableList');
  final exampleList = ObservableList.of([1, 2, 3]);
  exampleList.observe((_) => print('First list listener should never fire'));
  exampleList.observe((_) => print('Second list listener'), fireImmediately: true);
}
```

And output:
```
>>> ObservableSet
First set listener should never fire
Second set listener
First set listener should never fire
Second set listener
First set listener should never fire
Second set listener

>>> ObservableMap
First map listener should never fire
Second map listener
First map listener should never fire
Second map listener
First map listener should never fire
Second map listener

>>> ObservableList
Second list listener
```



Fix patch:
diff --git a/mobx/CHANGELOG.md b/mobx/CHANGELOG.md
index 52380b9d..3eabc66a 100644
--- a/mobx/CHANGELOG.md
+++ b/mobx/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.2.2
+
+- Fix [#956]((https://github.com/mobxjs/mobx.dart/issues/956)): ObservableSet` and `ObservableMap` should not notify all listeners when `observe` with fireImmediately. by [@amondnet](https://github.com/amondnet) in [#962](https://github.com/mobxjs/mobx.dart/pull/962)
+
 ## 2.2.1
 
 - Reduces unnecessary iterations while using `Iterables` with `addAll`, `insertAll`, `replaceRange`, `setAll` and `setRange` methods. [#942](https://github.com/mobxjs/mobx.dart/pull/942).
diff --git a/mobx/lib/src/api/observable_collections/observable_map.dart b/mobx/lib/src/api/observable_collections/observable_map.dart
index be1cd7d4..e5f31281 100644
--- a/mobx/lib/src/api/observable_collections/observable_map.dart
+++ b/mobx/lib/src/api/observable_collections/observable_map.dart
@@ -219,11 +219,17 @@ class ObservableMap<K, V>
   @override
   Dispose observe(MapChangeListener<K, V> listener,
       {bool fireImmediately = false}) {
-    final dispose = _listeners.add(listener);
     if (fireImmediately == true) {
-      _map.forEach(_reportAdd);
+      _map.forEach((key, value) {
+        listener(MapChange<K, V>(
+          type: OperationType.add,
+          key: key,
+          newValue: value,
+          object: this,
+        ));
+      });
     }
-    return dispose;
+    return _listeners.add(listener);
   }
 }
 
diff --git a/mobx/lib/src/api/observable_collections/observable_set.dart b/mobx/lib/src/api/observable_collections/observable_set.dart
index 65cc56e8..40944545 100644
--- a/mobx/lib/src/api/observable_collections/observable_set.dart
+++ b/mobx/lib/src/api/observable_collections/observable_set.dart
@@ -153,11 +153,16 @@ class ObservableSet<T>
   @override
   Dispose observe(SetChangeListener<T> listener,
       {bool fireImmediately = false}) {
-    final dispose = _listeners.add(listener);
     if (fireImmediately == true) {
-      _set.forEach(_reportAdd);
+      for (final value in _set) {
+        listener(SetChange(
+          object: this,
+          type: OperationType.add,
+          value: value,
+        ));
+      }
     }
-    return dispose;
+    return _listeners.add(listener);
   }
 
   void _reportAdd(T value) {
diff --git a/mobx/lib/version.dart b/mobx/lib/version.dart
index ebfac1c1..ff46c3cb 100644
--- a/mobx/lib/version.dart
+++ b/mobx/lib/version.dart
@@ -1,4 +1,4 @@
 // Generated via set_version.dart. !!!DO NOT MODIFY BY HAND!!!
 
 /// The current version as per `pubspec.yaml`.
-const version = '2.2.1';
+const version = '2.2.2';
diff --git a/mobx/pubspec.yaml b/mobx/pubspec.yaml
index bbf31ca5..e8abae7b 100644
--- a/mobx/pubspec.yaml
+++ b/mobx/pubspec.yaml
@@ -1,5 +1,5 @@
 name: mobx
-version: 2.2.1
+version: 2.2.2
 description: "MobX is a library for reactively managing the state of your applications. Use the power of observables, actions, and reactions to supercharge your Dart and Flutter apps."
 
 homepage: https://github.com/mobxjs/mobx.dart


Test patch:
diff --git a/mobx/test/observable_list_test.dart b/mobx/test/observable_list_test.dart
index 14fab84c..90553e53 100644
--- a/mobx/test/observable_list_test.dart
+++ b/mobx/test/observable_list_test.dart
@@ -103,6 +103,32 @@ void main() {
       expect(count, equals(2));
     });
 
+    test(
+        'observe with fireImmediately should not send events to already registered listeners',
+        () {
+      final list = ObservableList.of([0]);
+
+      var count1 = 0;
+      var count2 = 0;
+
+      list
+        .observe((change) {
+          count1++;
+        });
+
+      list
+        .observe((change) {
+          count2++;
+        }, fireImmediately: true);
+
+      list.add(1);
+
+      // 0 + 1: add
+      expect(count1, equals(1));
+      // 1 + 1: fireImmediately + add
+      expect(count2, equals(2));
+    });
+
     test('observe set length works', () {
       // ignore: omit_local_variable_types
       final ObservableList<int?> list = ObservableList.of([0]);
diff --git a/mobx/test/observable_map_test.dart b/mobx/test/observable_map_test.dart
index b057d83e..71a99559 100644
--- a/mobx/test/observable_map_test.dart
+++ b/mobx/test/observable_map_test.dart
@@ -222,6 +222,20 @@ void main() {
       expect(changes[1].newValue, equals(1));
     });
 
+    test(
+        'observe should not send changes to all listeners immediately when fireImmediately is true',
+        () {
+      final changes1 = <MapChange>[];
+      final changes2 = <MapChange>[];
+      final map = ObservableMap.of({'a': 0, 'b': 1});
+
+      map.observe(changes1.add);
+      map.observe(changes2.add, fireImmediately: true);
+
+      expect(changes1, isEmpty);
+      expect(changes2, isNotEmpty);
+    });
+
     test('works when adding a null value', () {
       final map = ObservableMap();
       map['a'] = null;
diff --git a/mobx/test/observable_set_test.dart b/mobx/test/observable_set_test.dart
index cf4180be..c5778463 100644
--- a/mobx/test/observable_set_test.dart
+++ b/mobx/test/observable_set_test.dart
@@ -86,6 +86,19 @@ void main() {
       expect(changes[1].value, equals(2));
     });
 
+    test(
+        'observe should not send add events to all listeners when fireImmediately is true',
+        () {
+      final oset = ObservableSet.of([1, 2]);
+      final changes1 = <SetChange<int>>[];
+      final changes2 = <SetChange<int>>[];
+      oset.observe(changes1.add);
+      oset.observe(changes2.add, fireImmediately: true);
+
+      expect(changes1, isEmpty);
+      expect(changes2, isNotEmpty);
+    });
+
     group('fires reportObserved() for read methods', () {
       <String, void Function(ObservableSet<int>)>{
         'union': (m) => m.union({2, 5, 6}),