From e5b6ca6b9ee7dcf5c9dc32b7491ffcfbb2fb206a Mon Sep 17 00:00:00 2001 From: naipaka Date: Mon, 6 Nov 2023 19:19:06 +0900 Subject: [PATCH] refactor: RemoteParameter class to improve listener management --- .../lib/src/remote_parameter.dart | 36 +++++++++++++++---- .../test/src/remote_parameter_test.dart | 27 +++++++++++++- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/packages/flutterfire_remote_parameter_fetcher/lib/src/remote_parameter.dart b/packages/flutterfire_remote_parameter_fetcher/lib/src/remote_parameter.dart index 8a55f84..bcab48c 100644 --- a/packages/flutterfire_remote_parameter_fetcher/lib/src/remote_parameter.dart +++ b/packages/flutterfire_remote_parameter_fetcher/lib/src/remote_parameter.dart @@ -1,16 +1,16 @@ +import 'dart:async'; + /// A class that holds the value of a parameter fetched from a remote. /// It also provides a Stream of updated parameter information. class RemoteParameter { RemoteParameter({ required T value, - required Stream onConfigUpdated, + required this.onConfigUpdated, required this.activateAndRefetch, - }) : _value = value { - onConfigUpdated.listen((_) async { - _value = await activateAndRefetch(); - _notifyListeners(); - }); - } + }) : _value = value; + + /// A Stream of updated parameter information. + Stream onConfigUpdated; /// A function that will activate the fetched config and refetch the value. /// This is useful for when you want to force a refetch of the value. @@ -22,10 +22,32 @@ class RemoteParameter { final List _listeners = []; + late StreamSubscription _subscription; + /// Add a listener to be notified when the value changes. /// Executed when the remote value is updated. void addListener(void Function(T value) listener) { _listeners.add(listener); + + if (_listeners.length == 1) { + // When the listener is added for the first time, + // monitor the onConfigUpdated stream. + _subscription = onConfigUpdated.listen((_) async { + _value = await activateAndRefetch(); + _notifyListeners(); + }); + } + } + + /// Remove a listener. + Future removeListener(void Function(T value) listener) async { + _listeners.remove(listener); + + if (_listeners.isEmpty) { + // When the last listener is removed, + // cancel the subscription to the onConfigUpdated stream. + await _subscription.cancel(); + } } void _notifyListeners() { diff --git a/packages/flutterfire_remote_parameter_fetcher/test/src/remote_parameter_test.dart b/packages/flutterfire_remote_parameter_fetcher/test/src/remote_parameter_test.dart index 996df09..73b2a44 100644 --- a/packages/flutterfire_remote_parameter_fetcher/test/src/remote_parameter_test.dart +++ b/packages/flutterfire_remote_parameter_fetcher/test/src/remote_parameter_test.dart @@ -38,6 +38,31 @@ void main() { expect(rp.value, 20); }); + test('should not call listener when removed', () async { + final controller = StreamController(); + addTearDown(controller.close); + + final rp = RemoteParameter( + value: 10, + onConfigUpdated: controller.stream, + activateAndRefetch: () async => 20, + ); + + var updatedValue = 10; + void listener(int value) { + updatedValue = value; + } + + rp.addListener(listener); + + await rp.removeListener(listener); + controller.add(null); + await Future.delayed(Duration.zero); + + expect(rp.value, 10); + expect(updatedValue, 10); + }); + test('should refetch value when config updates', () async { final controller = StreamController(); addTearDown(controller.close); @@ -50,7 +75,7 @@ void main() { refetchCalled = true; return 20; }, - ); + )..addListener((_) {}); controller.add(null);