From 8cc312b325bef3ca27e89a94d697d3d4507b7986 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Fri, 23 Oct 2026 12:40:29 -0400 Subject: [PATCH] chore!: make getFeatureFlagResult follow config.sendFeatureFlagEvent by default sendFeatureFlagEvent defaulted to true, so getFeatureFlagResult() captured $feature_flag_called even when PostHogConfig.sendFeatureFlagEvent was false, unlike getFeatureFlag(), isFeatureEnabled() and the native SDKs. It's now nullable, and null follows the config on every platform. --- .changeset/v6-flag-result-follows-config.md | 5 ++++ api/posthog_flutter.api.json | 6 ++--- .../posthog/flutter/PosthogFlutterPlugin.kt | 2 +- .../PosthogFlutterPlugin.swift | 9 +++++-- posthog_flutter/lib/posthog_flutter_web.dart | 8 +++--- .../lib/src/core/posthog_core.dart | 7 ++--- posthog_flutter/lib/src/posthog.dart | 9 ++++--- .../lib/src/posthog_flutter_desktop.dart | 2 +- .../lib/src/posthog_flutter_io.dart | 4 +-- .../posthog_flutter_platform_interface.dart | 2 +- .../lib/src/posthog_flutter_web_handler.dart | 4 +-- .../test/posthog_flutter_desktop_test.dart | 27 +++++++++++++++++++ .../test/posthog_flutter_io_test.dart | 2 +- ...sthog_flutter_platform_interface_fake.dart | 2 +- posthog_flutter/test/posthog_test.dart | 8 +++--- 15 files changed, 69 insertions(+), 28 deletions(-) create mode 100644 .changeset/v6-flag-result-follows-config.md diff --git a/.changeset/v6-flag-result-follows-config.md b/.changeset/v6-flag-result-follows-config.md new file mode 100644 index 00000000..c9b87b6c --- /dev/null +++ b/.changeset/v6-flag-result-follows-config.md @@ -0,0 +1,5 @@ +--- +"posthog_flutter": major +--- + +- **Breaking:** `getFeatureFlagResult()` now follows `PostHogConfig.sendFeatureFlagEvent` when `sendFeatureFlagEvent` is omitted, instead of always capturing `$feature_flag_called`. Pass `sendFeatureFlagEvent: true` to keep capturing the event when the config turns it off diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index d3767214..1499e991 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -1009,7 +1009,7 @@ "isRequired": false, "name": "sendFeatureFlagEvent", "relativePath": "lib/src/posthog_flutter_platform_interface.dart", - "typeName": "bool" + "typeName": "bool?" } ], "relativePath": "lib/src/posthog_flutter_platform_interface.dart", @@ -2062,7 +2062,7 @@ "isRequired": false, "name": "sendFeatureFlagEvent", "relativePath": "lib/src/posthog_flutter_platform_interface.dart", - "typeName": "bool" + "typeName": "bool?" } ], "relativePath": "lib/src/posthog_flutter_platform_interface.dart", @@ -4845,7 +4845,7 @@ "isRequired": false, "name": "sendFeatureFlagEvent", "relativePath": "lib/src/posthog.dart", - "typeName": "bool" + "typeName": "bool?" } ], "relativePath": "lib/src/posthog.dart", diff --git a/posthog_flutter/android/src/main/kotlin/com/posthog/flutter/PosthogFlutterPlugin.kt b/posthog_flutter/android/src/main/kotlin/com/posthog/flutter/PosthogFlutterPlugin.kt index f81e073c..f3c01a69 100644 --- a/posthog_flutter/android/src/main/kotlin/com/posthog/flutter/PosthogFlutterPlugin.kt +++ b/posthog_flutter/android/src/main/kotlin/com/posthog/flutter/PosthogFlutterPlugin.kt @@ -1685,7 +1685,7 @@ class PosthogFlutterPlugin : result.error("PosthogFlutterException", "Missing argument: key", null) return } - val sendEvent: Boolean = call.argument("sendEvent") ?: true + val sendEvent: Boolean? = call.argument("sendEvent") val flagResult = PostHog.getFeatureFlagResult(featureFlagKey, sendEvent) if (flagResult != null) { diff --git a/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift b/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift index 1fa099c4..3d32f8a3 100644 --- a/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift +++ b/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift @@ -1385,8 +1385,13 @@ extension PosthogFlutterPlugin { if let args = call.arguments as? [String: Any], let featureFlagKey = args["key"] as? String { - let sendEvent = args["sendEvent"] as? Bool ?? true - let flagResult = PostHogSDK.shared.getFeatureFlagResult(featureFlagKey, sendFeatureFlagEvent: sendEvent) + // A missing sendEvent follows config.sendFeatureFlagEvent. + let flagResult: PostHogFeatureFlagResult? + if let sendEvent = args["sendEvent"] as? Bool { + flagResult = PostHogSDK.shared.getFeatureFlagResult(featureFlagKey, sendFeatureFlagEvent: sendEvent) + } else { + flagResult = PostHogSDK.shared.getFeatureFlagResult(featureFlagKey) + } if let flagResult { result([ diff --git a/posthog_flutter/lib/posthog_flutter_web.dart b/posthog_flutter/lib/posthog_flutter_web.dart index 88047d82..2e783f2b 100644 --- a/posthog_flutter/lib/posthog_flutter_web.dart +++ b/posthog_flutter/lib/posthog_flutter_web.dart @@ -466,11 +466,13 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface { @override Future getFeatureFlagResult({ required String key, - bool sendFeatureFlagEvent = true, + bool? sendFeatureFlagEvent, }) async { final result = await handleWebMethodCall( - MethodCall('getFeatureFlagResult', - {'key': key, 'sendEvent': sendFeatureFlagEvent}), + MethodCall('getFeatureFlagResult', { + 'key': key, + if (sendFeatureFlagEvent != null) 'sendEvent': sendFeatureFlagEvent, + }), ); // Web SDK returns: { key, enabled, variant, payload } diff --git a/posthog_flutter/lib/src/core/posthog_core.dart b/posthog_flutter/lib/src/core/posthog_core.dart index 43f7ab84..62f9b566 100644 --- a/posthog_flutter/lib/src/core/posthog_core.dart +++ b/posthog_flutter/lib/src/core/posthog_core.dart @@ -771,10 +771,11 @@ abstract class PostHogCore extends PostHogCoreStateless { } /// Gets the result for a specific feature flag, capturing - /// `$feature_flag_called` when [sendEvent] is true. + /// `$feature_flag_called` when [sendEvent] is true, or when it is null and + /// `sendFeatureFlagEvent` is enabled in the config. PostHogFeatureFlagResult? getFeatureFlagResult( String key, { - required bool sendEvent, + bool? sendEvent, }) { final storedDetails = _getStoredFlagDetails(); final details = storedDetails?.toResponse(); @@ -783,7 +784,7 @@ abstract class PostHogCore extends PostHogCoreStateless { true; final featureFlag = details?.flags[key]; final flagValue = getFeatureFlagValue(featureFlag); - final shouldSendEvent = sendEvent && + final shouldSendEvent = (sendEvent ?? _sendFeatureFlagEvent) && !optedOut && !(_flagCallReported[key]?.contains(flagValue) ?? false); diff --git a/posthog_flutter/lib/src/posthog.dart b/posthog_flutter/lib/src/posthog.dart index 2c134fe6..495f1927 100644 --- a/posthog_flutter/lib/src/posthog.dart +++ b/posthog_flutter/lib/src/posthog.dart @@ -788,9 +788,10 @@ class Posthog { /// This is the canonical method for getting feature flag data. /// Returns `null` if the flag does not exist or cannot be loaded. /// - /// Set [sendFeatureFlagEvent] to `false` to suppress the - /// `$feature_flag_called` event. This is useful when you only need the - /// payload and do not want to emit the event. + /// [sendFeatureFlagEvent] controls whether this call captures the + /// `$feature_flag_called` event. Defaults to `null`, which follows + /// [PostHogConfig.sendFeatureFlagEvent]. Pass `false` when you only need the + /// payload, or `true` to capture the event even when the config turns it off. /// /// **Example:** /// ```dart @@ -802,7 +803,7 @@ class Posthog { /// ``` Future getFeatureFlagResult( String key, { - bool sendFeatureFlagEvent = true, + bool? sendFeatureFlagEvent, }) => _posthog.getFeatureFlagResult( key: key, sendFeatureFlagEvent: sendFeatureFlagEvent); diff --git a/posthog_flutter/lib/src/posthog_flutter_desktop.dart b/posthog_flutter/lib/src/posthog_flutter_desktop.dart index f8baec36..cf883e54 100644 --- a/posthog_flutter/lib/src/posthog_flutter_desktop.dart +++ b/posthog_flutter/lib/src/posthog_flutter_desktop.dart @@ -512,7 +512,7 @@ class PosthogFlutterDesktop extends PosthogFlutterPlatformInterface { @override Future getFeatureFlagResult({ required String key, - bool sendFeatureFlagEvent = true, + bool? sendFeatureFlagEvent, }) => _guardWith( 'getFeatureFlagResult', diff --git a/posthog_flutter/lib/src/posthog_flutter_io.dart b/posthog_flutter/lib/src/posthog_flutter_io.dart index f5a1b2ad..c69deb2c 100644 --- a/posthog_flutter/lib/src/posthog_flutter_io.dart +++ b/posthog_flutter/lib/src/posthog_flutter_io.dart @@ -716,7 +716,7 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { @override Future getFeatureFlagResult({ required String key, - bool sendFeatureFlagEvent = true, + bool? sendFeatureFlagEvent, }) async { if (!isSupportedPlatform()) { return null; @@ -725,7 +725,7 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { try { final result = await _methodChannel.invokeMethod('getFeatureFlagResult', { 'key': key, - 'sendEvent': sendFeatureFlagEvent, + if (sendFeatureFlagEvent != null) 'sendEvent': sendFeatureFlagEvent, }); // Native returns: { key, enabled, variant, payload } diff --git a/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart b/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart index 993ac9a3..a775d5f7 100644 --- a/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart +++ b/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart @@ -225,7 +225,7 @@ abstract class PosthogFlutterPlatformInterface extends PlatformInterface { Future getFeatureFlagResult({ required String key, - bool sendFeatureFlagEvent = true, + bool? sendFeatureFlagEvent, }) { throw UnimplementedError( 'getFeatureFlagResult() has not been implemented.', diff --git a/posthog_flutter/lib/src/posthog_flutter_web_handler.dart b/posthog_flutter/lib/src/posthog_flutter_web_handler.dart index 1e822330..d35dd08d 100644 --- a/posthog_flutter/lib/src/posthog_flutter_web_handler.dart +++ b/posthog_flutter/lib/src/posthog_flutter_web_handler.dart @@ -379,11 +379,11 @@ Future handleWebMethodCall(MethodCall call) async { return featureFlag?.dartify(); case 'getFeatureFlagResult': final key = args['key'] as String; - final sendEvent = args['sendEvent'] as bool? ?? true; + final sendEvent = args['sendEvent'] as bool?; final result = posthog?.getFeatureFlagResult( stringToJSAny(key), - {'send_event': sendEvent}.jsify(), + {if (sendEvent != null) 'send_event': sendEvent}.jsify(), ); return result?.dartify(); case 'getAllFeatureFlags': diff --git a/posthog_flutter/test/posthog_flutter_desktop_test.dart b/posthog_flutter/test/posthog_flutter_desktop_test.dart index c2f51696..36517dd8 100644 --- a/posthog_flutter/test/posthog_flutter_desktop_test.dart +++ b/posthog_flutter/test/posthog_flutter_desktop_test.dart @@ -1891,6 +1891,33 @@ void main() { expect(server.eventNames, isNot(contains(r'$feature_flag_called'))); }); + test( + r'getFeatureFlagResult follows config.sendFeatureFlagEvent unless overridden', + () async { + final server = await LocalPostHogServer.start(); + server.respond = (_) => PostHogResponse.json(_variantFlagResponse()); + final flagsLoaded = Completer(); + final config = configFor(server) + ..sendFeatureFlagEvent = false + ..onFeatureFlags = () { + if (!flagsLoaded.isCompleted) flagsLoaded.complete(); + }; + final platform = await setUpPlatform(config); + await flagsLoaded.future; + + expect( + (await platform.getFeatureFlagResult(key: 'variant-flag'))?.variant, + 'test-variant'); + await platform.capture(eventName: 'sentinel event'); + await server.waitForEvent('sentinel event'); + expect(server.eventNames, isNot(contains(r'$feature_flag_called'))); + + await platform.getFeatureFlagResult( + key: 'variant-flag', sendFeatureFlagEvent: true); + final called = await server.waitForEvent(r'$feature_flag_called'); + expect(_propertiesOf(called)[r'$feature_flag'], 'variant-flag'); + }); + test('onFeatureFlags reports the flags cached by the previous launch', () async { final server = await LocalPostHogServer.start(); diff --git a/posthog_flutter/test/posthog_flutter_io_test.dart b/posthog_flutter/test/posthog_flutter_io_test.dart index b33e9db0..76d1f39c 100644 --- a/posthog_flutter/test/posthog_flutter_io_test.dart +++ b/posthog_flutter/test/posthog_flutter_io_test.dart @@ -79,7 +79,7 @@ void main() { expect( await posthogFlutterIO.getFeatureFlagResult(key: 'missing'), isNull); expect(log.single.method, 'getFeatureFlagResult'); - expect(log.single.arguments, {'key': 'missing', 'sendEvent': true}); + expect(log.single.arguments, {'key': 'missing'}); }); }); diff --git a/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart b/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart index e4ad6a2b..b71a0bfe 100644 --- a/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart +++ b/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart @@ -298,7 +298,7 @@ class PosthogFlutterPlatformFake extends PosthogFlutterPlatformInterface { @override Future getFeatureFlagResult({ required String key, - bool sendFeatureFlagEvent = true, + bool? sendFeatureFlagEvent, }) async { getFeatureFlagResultCalls .add({'key': key, 'sendFeatureFlagEvent': sendFeatureFlagEvent}); diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index 4ddb2fa8..a3d9f7ad 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -480,7 +480,7 @@ void main() { final result = await Posthog().getFeatureFlagResult('non-existent'); expect(result, isNull); expect(fakePlatformInterface.getFeatureFlagResultCalls, [ - {'key': 'non-existent', 'sendFeatureFlagEvent': true} + {'key': 'non-existent', 'sendFeatureFlagEvent': null} ]); }); @@ -495,17 +495,17 @@ void main() { expect( await Posthog().getFeatureFlagResult('variant-flag'), same(expected)); expect(fakePlatformInterface.getFeatureFlagResultCalls, [ - {'key': 'variant-flag', 'sendFeatureFlagEvent': true} + {'key': 'variant-flag', 'sendFeatureFlagEvent': null} ]); }); - test('passes sendFeatureFlagEvent=true by default', () async { + test('leaves sendFeatureFlagEvent to the config by default', () async { await Posthog().getFeatureFlagResult('test'); expect( fakePlatformInterface .getFeatureFlagResultCalls.last['sendFeatureFlagEvent'], - isTrue, + isNull, ); });