Skip to content

Commit 8fea9e4

Browse files
authored
Merge branch 'main' into feat/start-stop-session-recording
2 parents d0df77e + 99acfb4 commit 8fea9e4

16 files changed

+572
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- `startSessionRecording({bool resumeCurrent = true})` Start session recording, optionally starting a new session
55
- `stopSessionRecording()` Stop the current session recording
66
- `isSessionReplayActive()` Check if session replay is currently active
7+
- feat: add `getFeatureFlagResult` API ([#279](https://github.com/PostHog/posthog-flutter/pull/279))
78

89
# 5.13.0
910

android/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ android {
5353
dependencies {
5454
testImplementation 'org.jetbrains.kotlin:kotlin-test'
5555
testImplementation 'org.mockito:mockito-core:5.0.0'
56-
// + Version 3.30.0 and the versions up to 4.0.0, not including 4.0.0 and higher
57-
implementation 'com.posthog:posthog-android:[3.30.0,4.0.0]'
56+
// + Version 3.31.0 and the versions up to 4.0.0, not including 4.0.0 and higher
57+
implementation 'com.posthog:posthog-android:[3.31.0,4.0.0]'
5858
}
5959

6060
testOptions {

android/src/main/kotlin/com/posthog/flutter/PosthogFlutterPlugin.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ class PosthogFlutterPlugin :
146146
getFeatureFlagPayload(call, result)
147147
}
148148

149+
"getFeatureFlagResult" -> {
150+
getFeatureFlagResult(call, result)
151+
}
152+
149153
"register" -> {
150154
register(call, result)
151155
}
@@ -387,6 +391,36 @@ class PosthogFlutterPlugin :
387391
}
388392
}
389393

394+
private fun getFeatureFlagResult(
395+
call: MethodCall,
396+
result: Result,
397+
) {
398+
try {
399+
val featureFlagKey = call.argument<String>("key")
400+
if (featureFlagKey.isNullOrEmpty()) {
401+
result.error("PosthogFlutterException", "Missing argument: key", null)
402+
return
403+
}
404+
val sendEvent: Boolean = call.argument("sendEvent") ?: true
405+
val flagResult = PostHog.getFeatureFlagResult(featureFlagKey, sendEvent)
406+
407+
if (flagResult != null) {
408+
result.success(
409+
mapOf(
410+
"key" to flagResult.key,
411+
"enabled" to flagResult.enabled,
412+
"variant" to flagResult.variant,
413+
"payload" to flagResult.payload,
414+
),
415+
)
416+
} else {
417+
result.success(null)
418+
}
419+
} catch (e: Throwable) {
420+
result.error("PosthogFlutterException", e.localizedMessage, null)
421+
}
422+
}
423+
390424
private fun identify(
391425
call: MethodCall,
392426
result: Result,

android/src/test/kotlin/com/posthog/flutter/PosthogFlutterPluginTest.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,19 @@ internal class PosthogFlutterPluginTest {
3939

4040
Mockito.verify(mockResult).success(true)
4141
}
42+
43+
@Test
44+
fun onMethodCall_getFeatureFlagResult_missingKey_returnsError() {
45+
val plugin = PosthogFlutterPlugin()
46+
47+
val call = MethodCall("getFeatureFlagResult", mapOf<String, Any>())
48+
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
49+
plugin.onMethodCall(call, mockResult)
50+
51+
Mockito.verify(mockResult).error(
52+
Mockito.eq("PosthogFlutterException"),
53+
Mockito.eq("Missing argument: key"),
54+
Mockito.isNull()
55+
)
56+
}
4257
}

example/lib/main.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,16 @@ class InitialScreenState extends State<InitialScreen> {
580580
},
581581
child: const Text("getFeatureFlagPayload"),
582582
),
583+
ElevatedButton(
584+
onPressed: () async {
585+
final result = await _posthogFlutterPlugin
586+
.getFeatureFlagResult("feature_name");
587+
setState(() {
588+
_result = result?.toString();
589+
});
590+
},
591+
child: const Text("getFeatureFlagResult"),
592+
),
583593
ElevatedButton(
584594
onPressed: () async {
585595
await _posthogFlutterPlugin.reloadFeatureFlags();

ios/Classes/PosthogFlutterPlugin.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin {
183183
isFeatureEnabled(call, result: result)
184184
case "getFeatureFlagPayload":
185185
getFeatureFlagPayload(call, result: result)
186+
case "getFeatureFlagResult":
187+
getFeatureFlagResult(call, result: result)
186188
case "identify":
187189
identify(call, result: result)
188190
case "capture":
@@ -557,6 +559,31 @@ extension PosthogFlutterPlugin {
557559
}
558560
}
559561

562+
private func getFeatureFlagResult(
563+
_ call: FlutterMethodCall,
564+
result: @escaping FlutterResult
565+
) {
566+
if let args = call.arguments as? [String: Any],
567+
let featureFlagKey = args["key"] as? String
568+
{
569+
let sendEvent = args["sendEvent"] as? Bool ?? true
570+
let flagResult = PostHogSDK.shared.getFeatureFlagResult(featureFlagKey, sendFeatureFlagEvent: sendEvent)
571+
572+
if let flagResult {
573+
result([
574+
"key": flagResult.key,
575+
"enabled": flagResult.enabled,
576+
"variant": flagResult.variant as Any,
577+
"payload": flagResult.payload as Any
578+
])
579+
} else {
580+
result(nil)
581+
}
582+
} else {
583+
_badArgumentError(result)
584+
}
585+
}
586+
560587
private func identify(
561588
_ call: FlutterMethodCall,
562589
result: @escaping FlutterResult

ios/posthog_flutter.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ Postog flutter plugin
2121
s.ios.dependency 'Flutter'
2222
s.osx.dependency 'FlutterMacOS'
2323

24-
# ~> Version 3.38.0 up to, but not including, 4.0.0
25-
s.dependency 'PostHog', '>= 3.38.0', '< 4.0.0'
24+
# ~> Version 3.40.0 up to, but not including, 4.0.0
25+
s.dependency 'PostHog', '>= 3.40.0', '< 4.0.0'
2626

2727
s.ios.deployment_target = '13.0'
2828
# PH iOS SDK 3.0.0 requires >= 10.15

lib/posthog_flutter.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
library posthog_flutter;
22

3+
export 'src/feature_flag_result.dart';
34
export 'src/posthog.dart';
45
export 'src/posthog_config.dart';
56
export 'src/posthog_event.dart';

lib/posthog_flutter_web.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:posthog_flutter/src/error_tracking/dart_exception_processor.dart
99
import 'package:posthog_flutter/src/util/logging.dart';
1010
import 'package:posthog_flutter/src/utils/property_normalizer.dart';
1111

12+
import 'src/feature_flag_result.dart';
1213
import 'src/posthog_config.dart';
1314
import 'src/posthog_flutter_platform_interface.dart';
1415
import 'src/posthog_flutter_web_handler.dart';
@@ -229,6 +230,18 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface {
229230
MethodCall('getFeatureFlagPayload', {'key': key}));
230231
}
231232

233+
@override
234+
Future<PostHogFeatureFlagResult?> getFeatureFlagResult({
235+
required String key,
236+
bool sendEvent = true,
237+
}) async {
238+
final result = await handleWebMethodCall(MethodCall(
239+
'getFeatureFlagResult', {'key': key, 'sendEvent': sendEvent}));
240+
241+
// Web SDK returns: { key, enabled, variant, payload }
242+
return PostHogFeatureFlagResult.fromMap(result, key);
243+
}
244+
232245
@override
233246
Future<void> flush() async {
234247
return handleWebMethodCall(const MethodCall('flush'));

lib/src/feature_flag_result.dart

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/// Represents the result of a feature flag evaluation.
2+
///
3+
/// Contains the flag key, whether it's enabled, the variant (for multivariate flags),
4+
/// and any associated payload.
5+
class PostHogFeatureFlagResult {
6+
/// The feature flag key.
7+
final String key;
8+
9+
/// Whether the flag is enabled.
10+
///
11+
/// For boolean flags, this is the flag value.
12+
/// For multivariate flags, this is true when the flag evaluates to any variant.
13+
final bool enabled;
14+
15+
/// The variant key for multivariate flags, or null for boolean flags.
16+
final String? variant;
17+
18+
/// The JSON payload associated with the flag, if any.
19+
final Object? payload;
20+
21+
const PostHogFeatureFlagResult({
22+
required this.key,
23+
required this.enabled,
24+
this.variant,
25+
this.payload,
26+
});
27+
28+
@override
29+
String toString() {
30+
return 'PostHogFeatureFlagResult(key: $key, enabled: $enabled, variant: $variant, payload: $payload)';
31+
}
32+
33+
@override
34+
bool operator ==(Object other) {
35+
if (identical(this, other)) return true;
36+
return other is PostHogFeatureFlagResult &&
37+
other.key == key &&
38+
other.enabled == enabled &&
39+
other.variant == variant;
40+
}
41+
42+
@override
43+
int get hashCode => Object.hash(key, enabled, variant);
44+
45+
/// Creates a [PostHogFeatureFlagResult] from a native SDK response map.
46+
///
47+
/// The [map] should contain: key, enabled, variant, payload.
48+
/// Falls back to [fallbackKey] if the map doesn't include a key.
49+
/// Returns null if [result] is null or not a Map.
50+
static PostHogFeatureFlagResult? fromMap(Object? result, String fallbackKey) {
51+
if (result == null) return null;
52+
if (result is! Map) return null;
53+
54+
return PostHogFeatureFlagResult(
55+
key: result['key'] as String? ?? fallbackKey,
56+
enabled: result['enabled'] as bool? ?? false,
57+
variant: result['variant'] as String?,
58+
payload: result['payload'],
59+
);
60+
}
61+
}

0 commit comments

Comments
 (0)