Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/flag-read-send-event-override.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"posthog_flutter": minor
---

Add an optional `sendFeatureFlagEvent` parameter to `getFeatureFlag()` and `isFeatureEnabled()` to override `PostHogConfig.sendFeatureFlagEvent` per call
54 changes: 54 additions & 0 deletions api/posthog_flutter.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,15 @@
"name": "key",
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
"typeName": "String"
},
{
"isDeprecated": false,
"isExperimental": false,
"isNamed": true,
"isRequired": false,
"name": "sendFeatureFlagEvent",
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
"typeName": "bool?"
}
],
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
Expand Down Expand Up @@ -961,6 +970,15 @@
"name": "key",
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
"typeName": "String"
},
{
"isDeprecated": false,
"isExperimental": false,
"isNamed": true,
"isRequired": false,
"name": "sendFeatureFlagEvent",
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
"typeName": "bool?"
}
],
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
Expand Down Expand Up @@ -1823,6 +1841,15 @@
"name": "key",
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
"typeName": "String"
},
{
"isDeprecated": false,
"isExperimental": false,
"isNamed": true,
"isRequired": false,
"name": "sendFeatureFlagEvent",
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
"typeName": "bool?"
}
],
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
Expand Down Expand Up @@ -2006,6 +2033,15 @@
"name": "key",
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
"typeName": "String"
},
{
"isDeprecated": false,
"isExperimental": false,
"isNamed": true,
"isRequired": false,
"name": "sendFeatureFlagEvent",
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
"typeName": "bool?"
}
],
"relativePath": "lib/src/posthog_flutter_platform_interface.dart",
Expand Down Expand Up @@ -4665,6 +4701,15 @@
"name": "key",
"relativePath": "lib/src/posthog.dart",
"typeName": "String"
},
{
"isDeprecated": false,
"isExperimental": false,
"isNamed": true,
"isRequired": false,
"name": "sendFeatureFlagEvent",
"relativePath": "lib/src/posthog.dart",
"typeName": "bool?"
}
],
"relativePath": "lib/src/posthog.dart",
Expand Down Expand Up @@ -4964,6 +5009,15 @@
"name": "key",
"relativePath": "lib/src/posthog.dart",
"typeName": "String"
},
{
"isDeprecated": false,
"isExperimental": false,
"isNamed": true,
"isRequired": false,
"name": "sendFeatureFlagEvent",
"relativePath": "lib/src/posthog.dart",
"typeName": "bool?"
}
],
"relativePath": "lib/src/posthog.dart",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1690,7 +1690,7 @@ class PosthogFlutterPlugin :
) {
try {
val featureFlagKey: String = call.argument("key")!!
val flag = PostHog.getFeatureFlag(featureFlagKey)
val flag = PostHog.getFeatureFlag(featureFlagKey, sendFeatureFlagEvent = call.argument("sendEvent"))
result.success(flag)
} catch (e: Throwable) {
result.error("PosthogFlutterException", e.localizedMessage, null)
Expand Down Expand Up @@ -1935,7 +1935,7 @@ class PosthogFlutterPlugin :
) {
try {
val key: String = call.argument("key")!!
val isEnabled = PostHog.isFeatureEnabled(key)
val isEnabled = PostHog.isFeatureEnabled(key, sendFeatureFlagEvent = call.argument("sendEvent"))
result.success(isEnabled)
} catch (e: Throwable) {
result.error("PosthogFlutterException", e.localizedMessage, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1360,7 +1360,13 @@ extension PosthogFlutterPlugin {
if let args = call.arguments as? [String: Any],
let featureFlagKey = args["key"] as? String
{
let value = PostHogSDK.shared.getFeatureFlag(featureFlagKey)
// A missing sendEvent follows config.sendFeatureFlagEvent.
let value: Any?
if let sendEvent = args["sendEvent"] as? Bool {
value = PostHogSDK.shared.getFeatureFlag(featureFlagKey, sendFeatureFlagEvent: sendEvent)
} else {
value = PostHogSDK.shared.getFeatureFlag(featureFlagKey)
}
result(value)
} else {
_badArgumentError(result)
Expand All @@ -1374,7 +1380,12 @@ extension PosthogFlutterPlugin {
if let args = call.arguments as? [String: Any],
let featureFlagKey = args["key"] as? String
{
let value = PostHogSDK.shared.isFeatureEnabled(featureFlagKey)
let value: Bool
if let sendEvent = args["sendEvent"] as? Bool {
value = PostHogSDK.shared.isFeatureEnabled(featureFlagKey, sendFeatureFlagEvent: sendEvent)
} else {
value = PostHogSDK.shared.isFeatureEnabled(featureFlagKey)
}
result(value)
} else {
_badArgumentError(result)
Expand Down
18 changes: 14 additions & 4 deletions posthog_flutter/lib/posthog_flutter_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -380,9 +380,13 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface {
}

@override
Future<bool> isFeatureEnabled(String key) async {
Future<bool> isFeatureEnabled(String key,
{bool? sendFeatureFlagEvent}) async {
final result = await handleWebMethodCall(
MethodCall('isFeatureEnabled', {'key': key}),
MethodCall('isFeatureEnabled', {
'key': key,
if (sendFeatureFlagEvent != null) 'sendEvent': sendFeatureFlagEvent,
}),
);
return result as bool? ?? false;
}
Expand Down Expand Up @@ -448,8 +452,14 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface {
}

@override
Future<Object?> getFeatureFlag({required String key}) async {
return handleWebMethodCall(MethodCall('getFeatureFlag', {'key': key}));
Future<Object?> getFeatureFlag({
required String key,
bool? sendFeatureFlagEvent,
}) async {
return handleWebMethodCall(MethodCall('getFeatureFlag', {
'key': key,
if (sendFeatureFlagEvent != null) 'sendEvent': sendFeatureFlagEvent,
}));
}

@override
Expand Down
17 changes: 10 additions & 7 deletions posthog_flutter/lib/src/core/posthog_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import 'uuid.dart';
/// It extends [PostHogCoreStateless] with state management for sessions,
/// identity, feature flags, and person profiles.
abstract class PostHogCore extends PostHogCoreStateless {
final bool _sendFeatureFlagEvents;
final bool _sendFeatureFlagEvent;
final PostHogPersonProfiles _personProfiles;
final List<String>? _evaluationContexts;

Expand Down Expand Up @@ -56,7 +56,7 @@ abstract class PostHogCore extends PostHogCoreStateless {
bool _flagsRequestAnnounced = false;

PostHogCore(PostHogConfig config, {required super.storage})
: _sendFeatureFlagEvents = config.sendFeatureFlagEvents,
: _sendFeatureFlagEvent = config.sendFeatureFlagEvent,
_personProfiles = config.personProfiles,
_evaluationContexts = config.evaluationContexts == null
? null
Expand Down Expand Up @@ -864,11 +864,14 @@ abstract class PostHogCore extends PostHogCoreStateless {
);
}

/// Gets a feature flag value.
/// Gets a feature flag value, capturing `$feature_flag_called` when
/// [sendEvent] is true, or when it is null and `sendFeatureFlagEvent` is
/// enabled in the config.
///
/// Returns null if the flag does not exist or has not been loaded.
PostHogFeatureFlagValue? getFeatureFlag(String key) {
final result = getFeatureFlagResult(key, sendEvent: _sendFeatureFlagEvents);
PostHogFeatureFlagValue? getFeatureFlag(String key, {bool? sendEvent}) {
final result = getFeatureFlagResult(key,
sendEvent: sendEvent ?? _sendFeatureFlagEvent);
if (result == null) return null;
return result.variant ?? result.enabled;
}
Expand All @@ -877,8 +880,8 @@ abstract class PostHogCore extends PostHogCoreStateless {
_getStoredFlagDetails()?.toResponse().featureFlags;

/// Checks if a feature flag is enabled.
bool? isFeatureEnabled(String key) {
final response = getFeatureFlag(key);
bool? isFeatureEnabled(String key, {bool? sendEvent}) {
final response = getFeatureFlag(key, sendEvent: sendEvent);
if (response == null) return null;
if (response is bool) return response;
return true; // String variants are truthy
Expand Down
36 changes: 33 additions & 3 deletions posthog_flutter/lib/src/posthog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,23 @@ class Posthog {
///
/// The [key] is the feature flag key.
///
/// [sendFeatureFlagEvent] controls whether this call captures the
/// `$feature_flag_called` event. Defaults to `null`, which follows
/// [PostHogConfig.sendFeatureFlagEvent]. Pass `false` to skip the event for
/// this call, or `true` to capture it even when the config turns it off.
///
/// Returns `false` when the flag is disabled, missing, or not a boolean flag.
Future<bool> isFeatureEnabled(String key) => _posthog.isFeatureEnabled(key);
///
/// **Example:**
/// ```dart
/// if (await Posthog().isFeatureEnabled('new-checkout',
/// sendFeatureFlagEvent: false)) {
/// // show the new checkout without recording a flag exposure
/// }
/// ```
Future<bool> isFeatureEnabled(String key, {bool? sendFeatureFlagEvent}) =>
_posthog.isFeatureEnabled(key,
sendFeatureFlagEvent: sendFeatureFlagEvent);

/// Reloads feature flags for the current user.
///
Expand Down Expand Up @@ -761,8 +776,23 @@ class Posthog {
/// Returns `null` if the flag does not exist or cannot be loaded. For boolean
/// flags, returns `true` or `false`. For multivariate flags, returns the
/// variant key.
Future<Object?> getFeatureFlag(String key) =>
_posthog.getFeatureFlag(key: key);
///
/// [sendFeatureFlagEvent] controls whether this call captures the
/// `$feature_flag_called` event. Defaults to `null`, which follows
/// [PostHogConfig.sendFeatureFlagEvent]. Pass `false` to skip the event for
/// this call, or `true` to capture it even when the config turns it off.
///
/// **Example:**
/// ```dart
/// final variant = await Posthog().getFeatureFlag('button-color',
/// sendFeatureFlagEvent: false);
/// if (variant == 'red') {
/// // show the red button without recording a flag exposure
/// }
/// ```
Future<Object?> getFeatureFlag(String key, {bool? sendFeatureFlagEvent}) =>
_posthog.getFeatureFlag(
key: key, sendFeatureFlagEvent: sendFeatureFlagEvent);

/// Returns the full feature flag result for [key], including value and
/// payload.
Expand Down
20 changes: 16 additions & 4 deletions posthog_flutter/lib/src/posthog_flutter_desktop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,13 @@ class PosthogFlutterDesktop extends PosthogFlutterPlatformInterface {
_guard('unregister', (client) => client.unregister(key));

@override
Future<bool> isFeatureEnabled(String key) => _guardWith('isFeatureEnabled',
false, (client) => client.isFeatureEnabled(key) ?? false);
Future<bool> isFeatureEnabled(String key, {bool? sendFeatureFlagEvent}) =>
_guardWith(
'isFeatureEnabled',
false,
(client) =>
client.isFeatureEnabled(key, sendEvent: sendFeatureFlagEvent) ??
false);

@override
Future<void> reloadFeatureFlags() => _guard(
Expand Down Expand Up @@ -494,8 +499,15 @@ class PosthogFlutterDesktop extends PosthogFlutterPlatformInterface {
});

@override
Future<Object?> getFeatureFlag({required String key}) => _guardWith<Object?>(
'getFeatureFlag', null, (client) => client.getFeatureFlag(key));
Future<Object?> getFeatureFlag({
required String key,
bool? sendFeatureFlagEvent,
}) =>
_guardWith<Object?>(
'getFeatureFlag',
null,
(client) =>
client.getFeatureFlag(key, sendEvent: sendFeatureFlagEvent));

@override
Future<Object?> getFeatureFlagPayload({required String key}) => _guardWith(
Expand Down
14 changes: 11 additions & 3 deletions posthog_flutter/lib/src/posthog_flutter_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -566,14 +566,16 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface {
}

@override
Future<bool> isFeatureEnabled(String key) async {
Future<bool> isFeatureEnabled(String key,
{bool? sendFeatureFlagEvent}) async {
if (!isSupportedPlatform()) {
return false;
}

try {
return await _methodChannel.invokeMethod('isFeatureEnabled', {
'key': key,
if (sendFeatureFlagEvent != null) 'sendEvent': sendFeatureFlagEvent,
});
} on PlatformException catch (exception) {
printIfDebug('Exeption on isFeatureEnabled: $exception');
Expand Down Expand Up @@ -691,13 +693,19 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface {
}

@override
Future<Object?> getFeatureFlag({required String key}) async {
Future<Object?> getFeatureFlag({
required String key,
bool? sendFeatureFlagEvent,
}) async {
if (!isSupportedPlatform()) {
return null;
}

try {
return await _methodChannel.invokeMethod('getFeatureFlag', {'key': key});
return await _methodChannel.invokeMethod('getFeatureFlag', {
'key': key,
if (sendFeatureFlagEvent != null) 'sendEvent': sendFeatureFlagEvent,
});
} on PlatformException catch (exception) {
printIfDebug('Exeption on getFeatureFlag: $exception');
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ abstract class PosthogFlutterPlatformInterface extends PlatformInterface {
throw UnimplementedError('unregister() has not been implemented.');
}

Future<bool> isFeatureEnabled(String key) {
Future<bool> isFeatureEnabled(String key, {bool? sendFeatureFlagEvent}) {
Comment thread
turnipdabeets marked this conversation as resolved.
Resolved
throw UnimplementedError('isFeatureEnabled() has not been implemented.');
}

Expand Down Expand Up @@ -216,7 +216,10 @@ abstract class PosthogFlutterPlatformInterface extends PlatformInterface {
throw UnimplementedError('group() has not been implemented.');
}

Future<Object?> getFeatureFlag({required String key}) {
Future<Object?> getFeatureFlag({
required String key,
bool? sendFeatureFlagEvent,
}) {
throw UnimplementedError('getFeatureFlag() has not been implemented.');
}

Expand Down
Loading
Loading