diff --git a/CHANGELOG.md b/CHANGELOG.md index 0903407..09726d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2.3.15 +* provide option to listen on source location stream which doesn't cause memory leaks + ## 2.3.14 * upgrade dependencies * fix for query results duplicate after 1250km as described in [this issue](https://github.com/beerstorm-net/GeoFlutterFire2/issues/13) diff --git a/README.md b/README.md index ead5fe6..26d83ef 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,12 @@ Calling `geoFirePoint.data` returns an object that contains a [geohash string](h ![](https://firebasestorage.googleapis.com/v0/b/geo-test-c92e4.appspot.com/o/point1.png?alt=media&token=0c833700-3dbd-476a-99a9-41c1143dbe97) ## Query Geo data +> **Warning** +> Querying locations creates 9 separated stream listeners on firestore. One for central geo hash and 8 surrounding it. +> `within` function creates a convenient broadcast stream, if you need more than one subscriber for the locations. But it also causes +> memory leak, because underlying stream listeners are not cancelled when you cancel StreamSubscription from `within` method. +> +> It's much safer to use withinAsSingleStreamSubscription which you can cancel, and it will also cancel 9 underlying streams as well. To query a collection of documents with 50kms from a point @@ -220,4 +226,4 @@ radius.add(25); ### Acknowledgements **NB! `GeoFlutterFire2` is a revisited and updated version of [GeoFlutterFire](https://github.com/DarshanGowda0/GeoFlutterFire)** -The work originates from [GeoFlutterFire](https://github.com/DarshanGowda0/GeoFlutterFire) by Darshan Narayanaswamy \ No newline at end of file +The work originates from [GeoFlutterFire](https://github.com/DarshanGowda0/GeoFlutterFire) by Darshan Narayanaswamy diff --git a/lib/src/collection.dart b/lib/src/collection.dart index 3c8d5d6..6dff236 100644 --- a/lib/src/collection.dart +++ b/lib/src/collection.dart @@ -77,9 +77,12 @@ class GeoFireCollectionRef { } } - /// query firestore documents based on geographic [radius] from geoFirePoint [center] - /// [field] specifies the name of the key in the document - Stream> within({ + /// Create combined stream listeners for each geo hash around (including) central point. + /// It creates 9 listeners and the hood. + /// + /// Returns [StreamController.stream] instance from rxdart, which is combined stream, + /// for all 9 listeners. + Stream> _buildQueryStream({ required GeoFirePoint center, required double radius, required String field, @@ -140,7 +143,54 @@ class GeoFireCollectionRef { }); return filteredList.map((element) => element.documentSnapshot).toList(); }); - return filtered.asBroadcastStream(); + return filtered; + } + + /// Query firestore documents based on geographic [radius] from geoFirePoint [center] + /// [field] specifies the name of the key in the document + /// returns merged stream as broadcast stream. + /// + /// Returns original stream from the underlying rxdart implementation, which + /// could be safely cancelled without memory leaks. It's single stream subscription, + /// so only single listener could be created at a time. + /// + /// This works the best if you have central point of listening for locations, + /// not per widget. + Stream> withinAsSingleStreamSubscription({ + required GeoFirePoint center, + required double radius, + required String field, + bool strictMode = false, + }) { + return _buildQueryStream( + center: center, + radius: radius, + field: field, + strictMode: strictMode, + ); + } + + /// Query firestore documents based on geographic [radius] from geoFirePoint [center] + /// [field] specifies the name of the key in the document + /// returns merged stream as broadcast stream. + /// + /// !WARNING! This causes memory leaks because under the hood rxdart StreamController + /// never causes it's subscriptions to be cancelled. So, even you cancel stream + /// subscription created from this method, under the hood listeners are still working. + /// + /// More at: https://github.com/dart-lang/sdk/issues/26686#issuecomment-225346901 + Stream> within({ + required GeoFirePoint center, + required double radius, + required String field, + bool strictMode = false, + }) { + return _buildQueryStream( + center: center, + radius: radius, + field: field, + strictMode: strictMode, + ).asBroadcastStream(); } Stream> mergeObservable(