From 849ffac3fd55222be003fec0b370963b8c84d01b Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Fri, 2 Oct 2026 19:02:45 -0400 Subject: [PATCH 01/12] chore!: remove deprecated APIs for v6 - Remove PostHogConfig.apiKey and the API_KEY manifest/Info.plist fallback. - Remove getFeatureFlagPayload() across Dart, web and native handlers. - Remove the debouncerDelay setter and PostHogDataMode.cellular. Co-Authored-By: Claude Opus 5.5 (1M context) --- .changeset/v6-remove-deprecated-apis.md | 8 ++++ .../posthog/flutter/PosthogFlutterPlugin.kt | 47 ++----------------- .../PosthogFlutterPlugin.swift | 36 ++------------ posthog_flutter/lib/posthog_flutter_web.dart | 7 --- posthog_flutter/lib/src/posthog.dart | 10 ---- posthog_flutter/lib/src/posthog_config.dart | 31 ------------ .../lib/src/posthog_flutter_io.dart | 16 ------- .../posthog_flutter_platform_interface.dart | 6 --- .../lib/src/posthog_flutter_web_handler.dart | 6 --- .../test/posthog_flutter_io_test.dart | 4 +- ...sthog_flutter_platform_interface_fake.dart | 6 --- posthog_flutter/test/posthog_test.dart | 2 - .../test/snapshots/setup_request.json | 1 - 13 files changed, 19 insertions(+), 161 deletions(-) create mode 100644 .changeset/v6-remove-deprecated-apis.md diff --git a/.changeset/v6-remove-deprecated-apis.md b/.changeset/v6-remove-deprecated-apis.md new file mode 100644 index 00000000..d291102a --- /dev/null +++ b/.changeset/v6-remove-deprecated-apis.md @@ -0,0 +1,8 @@ +--- +"posthog_flutter": major +--- + +- **Breaking:** remove `PostHogConfig.apiKey` and the `com.posthog.posthog.API_KEY` manifest/Info.plist key — use `projectToken` and `com.posthog.posthog.PROJECT_TOKEN` +- **Breaking:** remove `Posthog().getFeatureFlagPayload()` — use `getFeatureFlagResult(key, sendEvent: false)` and read `payload` +- **Breaking:** remove the `PostHogSessionReplayConfig.debouncerDelay` setter — use `PostHogSessionReplayConfig.throttleDelay` +- **Breaking:** remove `PostHogDataMode.cellular` — use `PostHogDataMode.any`, which it always behaved like 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 61909fa6..f81e073c 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 @@ -186,21 +186,10 @@ class PosthogFlutterPlugin : return } - val projectToken = - ( - bundle.getString("com.posthog.posthog.PROJECT_TOKEN") - ?: bundle.getString("com.posthog.posthog.API_KEY") - )?.trim() - - if (!bundle.containsKey("com.posthog.posthog.PROJECT_TOKEN") && bundle.containsKey("com.posthog.posthog.API_KEY")) { - Log.w( - "PostHog", - "com.posthog.posthog.API_KEY is deprecated and will be removed in the next major version. Use com.posthog.posthog.PROJECT_TOKEN instead!", - ) - } + val projectToken = bundle.getString("com.posthog.posthog.PROJECT_TOKEN")?.trim() if (projectToken.isNullOrEmpty()) { - Log.e("PostHog", "Either com.posthog.posthog.PROJECT_TOKEN or com.posthog.posthog.API_KEY must be provided!") + Log.e("PostHog", "com.posthog.posthog.PROJECT_TOKEN must be provided!") return } @@ -225,7 +214,6 @@ class PosthogFlutterPlugin : val posthogConfig = mutableMapOf() posthogConfig["projectToken"] = projectToken - posthogConfig["apiKey"] = projectToken posthogConfig["host"] = host posthogConfig["captureApplicationLifecycleEvents"] = captureApplicationLifecycleEvents posthogConfig["debug"] = debug @@ -331,10 +319,6 @@ class PosthogFlutterPlugin : getFeatureFlag(call, result) } - "getFeatureFlagPayload" -> { - getFeatureFlagPayload(call, result) - } - "getFeatureFlagResult" -> { getFeatureFlagResult(call, result) } @@ -565,19 +549,9 @@ class PosthogFlutterPlugin : } private fun setupPostHog(posthogConfig: Map) { - val projectToken = - ( - (posthogConfig["projectToken"] as String?) - ?: (posthogConfig["apiKey"] as String?) - )?.trim() - if (!posthogConfig.containsKey("projectToken") && posthogConfig.containsKey("apiKey")) { - Log.w( - "PostHog", - "apiKey is deprecated and will be removed in the next major version. Use projectToken instead!", - ) - } + val projectToken = (posthogConfig["projectToken"] as String?)?.trim() if (projectToken.isNullOrEmpty()) { - Log.e("PostHog", "Either projectToken or apiKey must be provided!") + Log.e("PostHog", "projectToken must be provided!") return } @@ -1701,19 +1675,6 @@ class PosthogFlutterPlugin : } } - private fun getFeatureFlagPayload( - call: MethodCall, - result: Result, - ) { - try { - val featureFlagKey: String = call.argument("key")!! - val flag = PostHog.getFeatureFlagResult(featureFlagKey, sendFeatureFlagEvent = false)?.payload - result.success(flag) - } catch (e: Throwable) { - result.error("PosthogFlutterException", e.localizedMessage, null) - } - } - private fun getFeatureFlagResult( call: MethodCall, result: Result, 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 002e2433..b60b392d 100644 --- a/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift +++ b/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift @@ -120,17 +120,11 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin { return } - let projectToken = ((Bundle.main.object(forInfoDictionaryKey: "com.posthog.posthog.PROJECT_TOKEN") as? String) ?? (Bundle.main.object(forInfoDictionaryKey: "com.posthog.posthog.API_KEY") as? String) ?? "") + let projectToken = (Bundle.main.object(forInfoDictionaryKey: "com.posthog.posthog.PROJECT_TOKEN") as? String ?? "") .trimmingCharacters(in: .whitespacesAndNewlines) - if Bundle.main.object(forInfoDictionaryKey: "com.posthog.posthog.PROJECT_TOKEN") == nil, - Bundle.main.object(forInfoDictionaryKey: "com.posthog.posthog.API_KEY") != nil - { - print("[PostHog] com.posthog.posthog.API_KEY is deprecated and will be removed in the next major version. Use com.posthog.posthog.PROJECT_TOKEN instead!") - } - if projectToken.isEmpty { - print("[PostHog] Either com.posthog.posthog.PROJECT_TOKEN or com.posthog.posthog.API_KEY must be provided!") + print("[PostHog] com.posthog.posthog.PROJECT_TOKEN must be provided!") return } @@ -144,7 +138,6 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin { setupPostHog([ "projectToken": projectToken, - "apiKey": projectToken, "host": host, "captureApplicationLifecycleEvents": captureApplicationLifecycleEvents, "debug": debug, @@ -179,13 +172,10 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin { print("[PostHog] Plugin instance not found!") return } - let projectToken = ((posthogConfig["projectToken"] as? String) ?? (posthogConfig["apiKey"] as? String) ?? "") + let projectToken = (posthogConfig["projectToken"] as? String ?? "") .trimmingCharacters(in: .whitespacesAndNewlines) - if posthogConfig["projectToken"] == nil, posthogConfig["apiKey"] != nil { - print("[PostHog] apiKey is deprecated and will be removed in the next major version. Use projectToken instead!") - } if projectToken.isEmpty { - print("[PostHog] Either projectToken or apiKey must be provided!") + print("[PostHog] projectToken must be provided!") return } @@ -256,7 +246,7 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin { switch dataMode { case "wifi": config.dataMode = .wifi - case "any", "cellular": + case "any": config.dataMode = .any default: break @@ -482,8 +472,6 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin { getFeatureFlag(call, result: result) case "isFeatureEnabled": isFeatureEnabled(call, result: result) - case "getFeatureFlagPayload": - getFeatureFlagPayload(call, result: result) case "getFeatureFlagResult": getFeatureFlagResult(call, result: result) case "getAllFeatureFlags": @@ -1394,20 +1382,6 @@ extension PosthogFlutterPlugin { } } - private func getFeatureFlagPayload( - _ call: FlutterMethodCall, - result: @escaping FlutterResult - ) { - if let args = call.arguments as? [String: Any], - let featureFlagKey = args["key"] as? String - { - let value = PostHogSDK.shared.getFeatureFlagResult(featureFlagKey, sendFeatureFlagEvent: false)?.payload - result(value) - } else { - _badArgumentError(result) - } - } - private func getFeatureFlagResult( _ call: FlutterMethodCall, result: @escaping FlutterResult diff --git a/posthog_flutter/lib/posthog_flutter_web.dart b/posthog_flutter/lib/posthog_flutter_web.dart index 2cda049d..0693d6d3 100644 --- a/posthog_flutter/lib/posthog_flutter_web.dart +++ b/posthog_flutter/lib/posthog_flutter_web.dart @@ -462,13 +462,6 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface { })); } - @override - Future getFeatureFlagPayload({required String key}) async { - return handleWebMethodCall( - MethodCall('getFeatureFlagPayload', {'key': key}), - ); - } - @override Future getFeatureFlagResult({ required String key, diff --git a/posthog_flutter/lib/src/posthog.dart b/posthog_flutter/lib/src/posthog.dart index b0d95263..4b2abb29 100644 --- a/posthog_flutter/lib/src/posthog.dart +++ b/posthog_flutter/lib/src/posthog.dart @@ -841,16 +841,6 @@ class Posthog { Future?> getAllFeatureFlags() => _posthog.getAllFeatureFlags(); - /// Returns the payload for the feature flag [key]. - /// - /// Returns `null` when the flag does not exist, has no payload, or the payload - /// cannot be loaded. - @Deprecated( - 'Use getFeatureFlagResult instead, which returns both value and payload.', - ) - Future getFeatureFlagPayload(String key) => - _posthog.getFeatureFlagPayload(key: key); - /// Captures a `$feature_view` event when the user sees the feature behind /// the feature flag [flag]. /// diff --git a/posthog_flutter/lib/src/posthog_config.dart b/posthog_flutter/lib/src/posthog_config.dart index 06461000..f478d237 100644 --- a/posthog_flutter/lib/src/posthog_config.dart +++ b/posthog_flutter/lib/src/posthog_config.dart @@ -72,17 +72,6 @@ enum PostHogDataMode { /// Send data only on Wi-Fi connections. wifi, - /// Send data on any available connection, same as [any]. - /// - /// Despite its name, this never restricted sending to cellular connections. - /// It will be removed in the next major version. - /// - /// ```dart - /// config.dataMode = PostHogDataMode.any; - /// ``` - @Deprecated('Behaves like `any`. Use `PostHogDataMode.any` instead.') - cellular, - /// Send data on any available connection. any, } @@ -98,16 +87,8 @@ class PostHogConfig { /// /// You can find it at: /// https://us.posthog.com/settings/project-details#variables - /// - /// This field was formerly named [apiKey]. final String projectToken; - /// Deprecated alias for [projectToken]. - @Deprecated( - 'Deprecated in favor of [projectToken]. This will be removed in the next major version.', - ) - String get apiKey => projectToken; - String _host = _defaultHost; /// The PostHog ingestion host. @@ -461,7 +442,6 @@ class PostHogConfig { Map toMap() { return { 'projectToken': projectToken, - 'apiKey': projectToken, 'host': host, 'flushAt': flushAt, 'maxQueueSize': maxQueueSize, @@ -899,17 +879,6 @@ class PostHogSessionReplayConfig { /// Default: null. PostHogTextMaskPolicy? textMaskPolicy; - /// Deprecated setter that forwards assigned values to [throttleDelay]. - /// - /// Debouncer delay used to reduce the number of snapshots captured and reduce - /// performance impact. This is used for capturing the view as a screenshot. - /// The lower the number, the more snapshots will be captured but higher the - /// performance impact. Defaults to 1s. - @Deprecated('Deprecated in favor of [throttleDelay] from v4.8.0.') - set debouncerDelay(Duration debouncerDelay) { - throttleDelay = debouncerDelay; - } - /// Throttling delay used to reduce the number of snapshots captured and reduce /// performance impact. /// diff --git a/posthog_flutter/lib/src/posthog_flutter_io.dart b/posthog_flutter/lib/src/posthog_flutter_io.dart index 96936643..808cc37a 100644 --- a/posthog_flutter/lib/src/posthog_flutter_io.dart +++ b/posthog_flutter/lib/src/posthog_flutter_io.dart @@ -712,22 +712,6 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { } } - @override - Future getFeatureFlagPayload({required String key}) async { - if (!isSupportedPlatform()) { - return null; - } - - try { - return await _methodChannel.invokeMethod('getFeatureFlagPayload', { - 'key': key, - }); - } on PlatformException catch (exception) { - printIfDebug('Exeption on getFeatureFlagPayload: $exception'); - return null; - } - } - @override Future getFeatureFlagResult({ required String key, diff --git a/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart b/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart index 565e8011..44a4f5b4 100644 --- a/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart +++ b/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart @@ -223,12 +223,6 @@ abstract class PosthogFlutterPlatformInterface extends PlatformInterface { throw UnimplementedError('getFeatureFlag() has not been implemented.'); } - Future getFeatureFlagPayload({required String key}) { - throw UnimplementedError( - 'getFeatureFlagPayload() has not been implemented.', - ); - } - Future getFeatureFlagResult({ required String key, bool sendEvent = true, diff --git a/posthog_flutter/lib/src/posthog_flutter_web_handler.dart b/posthog_flutter/lib/src/posthog_flutter_web_handler.dart index 139976d8..1e822330 100644 --- a/posthog_flutter/lib/src/posthog_flutter_web_handler.dart +++ b/posthog_flutter/lib/src/posthog_flutter_web_handler.dart @@ -57,7 +57,6 @@ extension PostHogExtension on PostHog { // ignore: non_constant_identifier_names external bool has_opted_out_capturing(); external JSAny? getFeatureFlag(JSAny key, [JSAny? options]); - external JSAny? getFeatureFlagPayload(JSAny key); external JSAny? getFeatureFlagResult(JSAny key, [JSAny? options]); // Added in posthog-js 1.395.0; the call site guards with try/catch. external JSAny? getAllFeatureFlags(); @@ -378,11 +377,6 @@ Future handleWebMethodCall(MethodCall call) async { final featureFlag = posthog?.getFeatureFlag(stringToJSAny(key), _flagOptions(args)); return featureFlag?.dartify(); - case 'getFeatureFlagPayload': - final key = args['key'] as String; - - final featureFlag = posthog?.getFeatureFlagPayload(stringToJSAny(key)); - return featureFlag?.dartify(); case 'getFeatureFlagResult': final key = args['key'] as String; final sendEvent = args['sendEvent'] as bool? ?? true; diff --git a/posthog_flutter/test/posthog_flutter_io_test.dart b/posthog_flutter/test/posthog_flutter_io_test.dart index 37c84911..4e5fa2db 100644 --- a/posthog_flutter/test/posthog_flutter_io_test.dart +++ b/posthog_flutter/test/posthog_flutter_io_test.dart @@ -186,14 +186,14 @@ void main() { }, ); - test('setup sends projectToken and deprecated apiKey alias', () async { + test('setup sends projectToken', () async { testConfig = PostHogConfig(' \n test_project_token\t '); await posthogFlutterIO.setup(testConfig); final call = log.firstWhere((c) => c.method == 'setup'); final args = Map.from(call.arguments as Map); expect(args['projectToken'], equals('test_project_token')); - expect(args['apiKey'], equals('test_project_token')); + expect(args.containsKey('apiKey'), isFalse); }); test( diff --git a/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart b/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart index 64083a7e..f99211d4 100644 --- a/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart +++ b/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart @@ -95,7 +95,6 @@ class PosthogFlutterPlatformFake extends PosthogFlutterPlatformInterface { // Feature flag test data final Map featureFlagValues = {}; - final Map featureFlagPayloads = {}; final List> flagReadCalls = []; @@ -294,11 +293,6 @@ class PosthogFlutterPlatformFake extends PosthogFlutterPlatformInterface { return featureFlagValues[key] == true; } - @override - Future getFeatureFlagPayload({required String key}) async { - return featureFlagPayloads[key]; - } - PostHogFeatureFlagResult? featureFlagResult; @override diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index 727d2f39..173a3744 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -237,10 +237,8 @@ void main() { config.host = ' \nhttps://eu.i.posthog.com/\t '; expect(config.projectToken, equals('test_project_token')); - expect(config.apiKey, equals('test_project_token')); expect(config.host, equals('https://eu.i.posthog.com/')); expect(config.toMap()['projectToken'], equals('test_project_token')); - expect(config.toMap()['apiKey'], equals('test_project_token')); expect(config.toMap()['host'], equals('https://eu.i.posthog.com/')); }); diff --git a/posthog_flutter/test/snapshots/setup_request.json b/posthog_flutter/test/snapshots/setup_request.json index 58543998..367138b4 100644 --- a/posthog_flutter/test/snapshots/setup_request.json +++ b/posthog_flutter/test/snapshots/setup_request.json @@ -3,7 +3,6 @@ "method": "setup", "arguments": { "projectToken": "snapshot_project_token", - "apiKey": "snapshot_project_token", "host": "https://eu.i.posthog.com", "flushAt": 12, "maxQueueSize": 800, From 6cf94a1cfd7d3221d27598a3326cfb12b5a012bf Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Mon, 5 Oct 2026 16:01:33 -0400 Subject: [PATCH 02/12] chore(android): declare minSdkVersion 23 to match posthog-android posthog-android has required minSdk 23 since 3.39.0, and the plugin's dependency range [3.71.0, 4.0.0) only resolves to versions that need it. Apps below 23 already fail the manifest merge, so this only corrects the declared value. Co-Authored-By: Claude Opus 5.5 (1M context) --- .changeset/v6-remove-deprecated-apis.md | 1 + api/posthog_flutter.api.json | 2 +- posthog_flutter/android/build.gradle | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.changeset/v6-remove-deprecated-apis.md b/.changeset/v6-remove-deprecated-apis.md index d291102a..828de674 100644 --- a/.changeset/v6-remove-deprecated-apis.md +++ b/.changeset/v6-remove-deprecated-apis.md @@ -6,3 +6,4 @@ - **Breaking:** remove `Posthog().getFeatureFlagPayload()` — use `getFeatureFlagResult(key, sendEvent: false)` and read `payload` - **Breaking:** remove the `PostHogSessionReplayConfig.debouncerDelay` setter — use `PostHogSessionReplayConfig.throttleDelay` - **Breaking:** remove `PostHogDataMode.cellular` — use `PostHogDataMode.any`, which it always behaved like +- Declare Android `minSdkVersion` 23 to match `posthog-android`, which has required it since 3.39.0. Apps on minSdk 21 or 22 already could not build with this plugin, so nothing changes for them diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index 89de6266..a153134b 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -13,7 +13,7 @@ "packageApi": { "androidPlatformConstraints": { "compileSdkVersion": null, - "minSdkVersion": 21, + "minSdkVersion": 23, "targetSdkVersion": null }, "executableDeclarations": [ diff --git a/posthog_flutter/android/build.gradle b/posthog_flutter/android/build.gradle index ba6dcb54..2ae6e792 100644 --- a/posthog_flutter/android/build.gradle +++ b/posthog_flutter/android/build.gradle @@ -58,7 +58,7 @@ android { } defaultConfig { - minSdkVersion 21 + minSdkVersion 23 } dependencies { From 1da21d52694586a92506b23e83907c693915e42f Mon Sep 17 00:00:00 2001 From: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com> Date: Tue, 6 Oct 2026 20:35:01 -0400 Subject: [PATCH 03/12] chore!: remove enable/disable and sendFeatureFlagEvents (#638) Removes the names deprecated in 5.x in favor of optIn/optOut and sendFeatureFlagEvent. Co-authored-by: Claude Opus 5.5 (1M context) --- .changeset/v6-remove-5x-deprecations.md | 6 ++++ api/posthog_flutter.api.json | 35 --------------------- posthog_flutter/lib/src/posthog.dart | 12 ------- posthog_flutter/lib/src/posthog_config.dart | 11 ------- posthog_flutter/test/posthog_test.dart | 10 ------ 5 files changed, 6 insertions(+), 68 deletions(-) create mode 100644 .changeset/v6-remove-5x-deprecations.md diff --git a/.changeset/v6-remove-5x-deprecations.md b/.changeset/v6-remove-5x-deprecations.md new file mode 100644 index 00000000..7cde3baa --- /dev/null +++ b/.changeset/v6-remove-5x-deprecations.md @@ -0,0 +1,6 @@ +--- +"posthog_flutter": major +--- + +- **Breaking:** remove `Posthog().enable()` and `Posthog().disable()` — use `optIn()` and `optOut()` +- **Breaking:** remove `PostHogConfig.sendFeatureFlagEvents` — use `sendFeatureFlagEvent` diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index a153134b..7b4b709c 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -2464,17 +2464,6 @@ "relativePath": "lib/src/posthog_config.dart", "typeName": "bool" }, - { - "entryPoints": [], - "isDeprecated": true, - "isExperimental": false, - "isReadable": true, - "isStatic": false, - "isWriteable": true, - "name": "sendFeatureFlagEvents", - "relativePath": "lib/src/posthog_config.dart", - "typeName": "bool" - }, { "entryPoints": [], "isDeprecated": false, @@ -4599,30 +4588,6 @@ "type": "method", "typeParameterNames": [] }, - { - "entryPoints": [], - "isDeprecated": true, - "isExperimental": false, - "isStatic": false, - "name": "disable", - "parameters": [], - "relativePath": "lib/src/posthog.dart", - "returnTypeName": "Future", - "type": "method", - "typeParameterNames": [] - }, - { - "entryPoints": [], - "isDeprecated": true, - "isExperimental": false, - "isStatic": false, - "name": "enable", - "parameters": [], - "relativePath": "lib/src/posthog.dart", - "returnTypeName": "Future", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, diff --git a/posthog_flutter/lib/src/posthog.dart b/posthog_flutter/lib/src/posthog.dart index 4b2abb29..72776c38 100644 --- a/posthog_flutter/lib/src/posthog.dart +++ b/posthog_flutter/lib/src/posthog.dart @@ -436,18 +436,6 @@ class Posthog { } } - /// Opts the current user out of data collection. - @Deprecated( - 'Use optOut() instead. This will be removed in the next major version.', - ) - Future disable() => optOut(); - - /// Opts the current user back in to data collection. - @Deprecated( - 'Use optIn() instead. This will be removed in the next major version.', - ) - Future enable() => optIn(); - /// Returns whether the current user has opted out of data collection. Future isOptOut() => _posthog.isOptOut(); diff --git a/posthog_flutter/lib/src/posthog_config.dart b/posthog_flutter/lib/src/posthog_config.dart index f478d237..1b84f7f1 100644 --- a/posthog_flutter/lib/src/posthog_config.dart +++ b/posthog_flutter/lib/src/posthog_config.dart @@ -124,17 +124,6 @@ class PostHogConfig { /// Defaults to `true`. var sendFeatureFlagEvent = true; - /// Deprecated alias for [sendFeatureFlagEvent]. - @Deprecated( - 'Use sendFeatureFlagEvent instead. This will be removed in the next major version.', - ) - bool get sendFeatureFlagEvents => sendFeatureFlagEvent; - - @Deprecated( - 'Use sendFeatureFlagEvent instead. This will be removed in the next major version.', - ) - set sendFeatureFlagEvents(bool value) => sendFeatureFlagEvent = value; - /// Whether feature flags are loaded when the SDK starts. /// /// Defaults to `true`, which means every SDK start issues a `/flags` request. diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index 173a3744..8b2ad177 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -214,16 +214,6 @@ void main() { }); group('PostHogConfig', () { - test('sendFeatureFlagEvents forwards to sendFeatureFlagEvent', () { - final config = PostHogConfig('test_project_token'); - - // ignore: deprecated_member_use_from_same_package - config.sendFeatureFlagEvents = false; - - expect(config.sendFeatureFlagEvent, isFalse); - expect(config.toMap()['sendFeatureFlagEvents'], isFalse); - }); - test('toMap forwards evaluationContexts only when set', () { final config = PostHogConfig('test_project_token'); expect(config.toMap().containsKey('evaluationContexts'), isFalse); From 3fb2a60e7d14258886a3982c3f88e626e49cea2f Mon Sep 17 00:00:00 2001 From: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:57:55 -0400 Subject: [PATCH 04/12] chore!: rename getFeatureFlagResult sendEvent to sendFeatureFlagEvent (#647) Matches PostHogConfig.sendFeatureFlagEvent and the iOS/Android getFeatureFlagResult parameter. The method-channel key stays 'sendEvent'. Co-authored-by: Claude Opus 5.5 (1M context) --- .changeset/v6-remove-deprecated-apis.md | 2 +- .changeset/v6-send-feature-flag-event-param.md | 5 +++++ api/posthog_flutter.api.json | 6 +++--- posthog_flutter/lib/posthog_flutter_web.dart | 5 +++-- posthog_flutter/lib/src/posthog.dart | 11 ++++++----- posthog_flutter/lib/src/posthog_flutter_io.dart | 4 ++-- .../src/posthog_flutter_platform_interface.dart | 2 +- .../test/posthog_flutter_io_test.dart | 2 +- .../posthog_flutter_platform_interface_fake.dart | 5 +++-- posthog_flutter/test/posthog_test.dart | 16 +++++++++------- 10 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 .changeset/v6-send-feature-flag-event-param.md diff --git a/.changeset/v6-remove-deprecated-apis.md b/.changeset/v6-remove-deprecated-apis.md index 828de674..52ef0668 100644 --- a/.changeset/v6-remove-deprecated-apis.md +++ b/.changeset/v6-remove-deprecated-apis.md @@ -3,7 +3,7 @@ --- - **Breaking:** remove `PostHogConfig.apiKey` and the `com.posthog.posthog.API_KEY` manifest/Info.plist key — use `projectToken` and `com.posthog.posthog.PROJECT_TOKEN` -- **Breaking:** remove `Posthog().getFeatureFlagPayload()` — use `getFeatureFlagResult(key, sendEvent: false)` and read `payload` +- **Breaking:** remove `Posthog().getFeatureFlagPayload()` — use `getFeatureFlagResult(key, sendFeatureFlagEvent: false)` and read `payload` - **Breaking:** remove the `PostHogSessionReplayConfig.debouncerDelay` setter — use `PostHogSessionReplayConfig.throttleDelay` - **Breaking:** remove `PostHogDataMode.cellular` — use `PostHogDataMode.any`, which it always behaved like - Declare Android `minSdkVersion` 23 to match `posthog-android`, which has required it since 3.39.0. Apps on minSdk 21 or 22 already could not build with this plugin, so nothing changes for them diff --git a/.changeset/v6-send-feature-flag-event-param.md b/.changeset/v6-send-feature-flag-event-param.md new file mode 100644 index 00000000..677d86fb --- /dev/null +++ b/.changeset/v6-send-feature-flag-event-param.md @@ -0,0 +1,5 @@ +--- +"posthog_flutter": major +--- + +- **Breaking:** `getFeatureFlagResult(sendEvent:)` is now `getFeatureFlagResult(sendFeatureFlagEvent:)`, matching `PostHogConfig.sendFeatureFlagEvent` and the native SDKs diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index 7b4b709c..9a054995 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -1029,7 +1029,7 @@ "isExperimental": false, "isNamed": true, "isRequired": false, - "name": "sendEvent", + "name": "sendFeatureFlagEvent", "relativePath": "lib/src/posthog_flutter_platform_interface.dart", "typeName": "bool" } @@ -2104,7 +2104,7 @@ "isExperimental": false, "isNamed": true, "isRequired": false, - "name": "sendEvent", + "name": "sendFeatureFlagEvent", "relativePath": "lib/src/posthog_flutter_platform_interface.dart", "typeName": "bool" } @@ -5035,7 +5035,7 @@ "isExperimental": false, "isNamed": true, "isRequired": false, - "name": "sendEvent", + "name": "sendFeatureFlagEvent", "relativePath": "lib/src/posthog.dart", "typeName": "bool" } diff --git a/posthog_flutter/lib/posthog_flutter_web.dart b/posthog_flutter/lib/posthog_flutter_web.dart index 0693d6d3..e62c23af 100644 --- a/posthog_flutter/lib/posthog_flutter_web.dart +++ b/posthog_flutter/lib/posthog_flutter_web.dart @@ -465,10 +465,11 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface { @override Future getFeatureFlagResult({ required String key, - bool sendEvent = true, + bool sendFeatureFlagEvent = true, }) async { final result = await handleWebMethodCall( - MethodCall('getFeatureFlagResult', {'key': key, 'sendEvent': sendEvent}), + MethodCall('getFeatureFlagResult', + {'key': key, 'sendEvent': sendFeatureFlagEvent}), ); // Web SDK returns: { key, enabled, variant, payload } diff --git a/posthog_flutter/lib/src/posthog.dart b/posthog_flutter/lib/src/posthog.dart index 72776c38..2e315168 100644 --- a/posthog_flutter/lib/src/posthog.dart +++ b/posthog_flutter/lib/src/posthog.dart @@ -788,9 +788,9 @@ 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 [sendEvent] 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. + /// 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. /// /// **Example:** /// ```dart @@ -802,9 +802,10 @@ class Posthog { /// ``` Future getFeatureFlagResult( String key, { - bool sendEvent = true, + bool sendFeatureFlagEvent = true, }) => - _posthog.getFeatureFlagResult(key: key, sendEvent: sendEvent); + _posthog.getFeatureFlagResult( + key: key, sendFeatureFlagEvent: sendFeatureFlagEvent); /// Returns the results of all currently loaded feature flags. /// diff --git a/posthog_flutter/lib/src/posthog_flutter_io.dart b/posthog_flutter/lib/src/posthog_flutter_io.dart index 808cc37a..386e2a34 100644 --- a/posthog_flutter/lib/src/posthog_flutter_io.dart +++ b/posthog_flutter/lib/src/posthog_flutter_io.dart @@ -715,7 +715,7 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { @override Future getFeatureFlagResult({ required String key, - bool sendEvent = true, + bool sendFeatureFlagEvent = true, }) async { if (!isSupportedPlatform()) { return null; @@ -724,7 +724,7 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { try { final result = await _methodChannel.invokeMethod('getFeatureFlagResult', { 'key': key, - 'sendEvent': sendEvent, + '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 44a4f5b4..0e9e7cd2 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 sendEvent = true, + bool sendFeatureFlagEvent = true, }) { throw UnimplementedError( 'getFeatureFlagResult() has not been implemented.', diff --git a/posthog_flutter/test/posthog_flutter_io_test.dart b/posthog_flutter/test/posthog_flutter_io_test.dart index 4e5fa2db..b33e9db0 100644 --- a/posthog_flutter/test/posthog_flutter_io_test.dart +++ b/posthog_flutter/test/posthog_flutter_io_test.dart @@ -67,7 +67,7 @@ void main() { }; }); final result = await posthogFlutterIO.getFeatureFlagResult( - key: 'flag', sendEvent: false); + key: 'flag', sendFeatureFlagEvent: false); expect(result, isNotNull); expect(result!.key, 'flag'); expect(result.enabled, enabled); diff --git a/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart b/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart index f99211d4..66e35d79 100644 --- a/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart +++ b/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart @@ -298,9 +298,10 @@ class PosthogFlutterPlatformFake extends PosthogFlutterPlatformInterface { @override Future getFeatureFlagResult({ required String key, - bool sendEvent = true, + bool sendFeatureFlagEvent = true, }) async { - getFeatureFlagResultCalls.add({'key': key, 'sendEvent': sendEvent}); + getFeatureFlagResultCalls + .add({'key': key, 'sendFeatureFlagEvent': sendFeatureFlagEvent}); return featureFlagResult; } diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index 8b2ad177..84f138a3 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -479,7 +479,7 @@ void main() { final result = await Posthog().getFeatureFlagResult('non-existent'); expect(result, isNull); expect(fakePlatformInterface.getFeatureFlagResultCalls, [ - {'key': 'non-existent', 'sendEvent': true} + {'key': 'non-existent', 'sendFeatureFlagEvent': true} ]); }); @@ -494,24 +494,26 @@ void main() { expect( await Posthog().getFeatureFlagResult('variant-flag'), same(expected)); expect(fakePlatformInterface.getFeatureFlagResultCalls, [ - {'key': 'variant-flag', 'sendEvent': true} + {'key': 'variant-flag', 'sendFeatureFlagEvent': true} ]); }); - test('passes sendEvent=true by default', () async { + test('passes sendFeatureFlagEvent=true by default', () async { await Posthog().getFeatureFlagResult('test'); expect( - fakePlatformInterface.getFeatureFlagResultCalls.last['sendEvent'], + fakePlatformInterface + .getFeatureFlagResultCalls.last['sendFeatureFlagEvent'], isTrue, ); }); - test('passes sendEvent=false when specified', () async { - await Posthog().getFeatureFlagResult('test', sendEvent: false); + test('passes sendFeatureFlagEvent=false when specified', () async { + await Posthog().getFeatureFlagResult('test', sendFeatureFlagEvent: false); expect( - fakePlatformInterface.getFeatureFlagResultCalls.last['sendEvent'], + fakePlatformInterface + .getFeatureFlagResultCalls.last['sendFeatureFlagEvent'], isFalse, ); }); From a7737f323c68f018203d39f556cd35227f979bae Mon Sep 17 00:00:00 2001 From: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:22:25 -0400 Subject: [PATCH 05/12] chore!: rename platform enable/disable to optIn/optOut (#645) * chore!: rename platform enable/disable to optIn/optOut PosthogFlutterPlatformInterface.enable()/disable() become optIn()/optOut() to match the public Posthog API renamed in #636/#638. The method-channel names sent to native stay 'enable'/'disable'. Co-Authored-By: Claude Opus 5.5 (1M context) * docs(changeset): cover direct platform callers in the opt-in/out rename Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Opus 5.5 (1M context) --- .changeset/v6-platform-opt-in-out.md | 5 +++++ api/posthog_flutter.api.json | 8 ++++---- posthog_flutter/lib/posthog_flutter_web.dart | 4 ++-- posthog_flutter/lib/src/posthog.dart | 4 ++-- posthog_flutter/lib/src/posthog_flutter_io.dart | 4 ++-- .../lib/src/posthog_flutter_platform_interface.dart | 8 ++++---- .../test/posthog_flutter_platform_interface_fake.dart | 4 ++-- 7 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 .changeset/v6-platform-opt-in-out.md diff --git a/.changeset/v6-platform-opt-in-out.md b/.changeset/v6-platform-opt-in-out.md new file mode 100644 index 00000000..0bdcaddf --- /dev/null +++ b/.changeset/v6-platform-opt-in-out.md @@ -0,0 +1,5 @@ +--- +"posthog_flutter": major +--- + +- **Breaking:** rename `enable()`/`disable()` to `optIn()`/`optOut()` on `PosthogFlutterPlatformInterface`, `PosthogFlutterIO` and `PosthogFlutterWeb`. Update direct calls to these platform classes, custom implementations, and test fakes/mocks. Calls to `Posthog().optIn()`/`optOut()` are unchanged diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index 9a054995..115b5ec9 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -657,7 +657,7 @@ "isDeprecated": false, "isExperimental": false, "isStatic": false, - "name": "disable", + "name": "optOut", "parameters": [], "relativePath": "lib/src/posthog_flutter_platform_interface.dart", "returnTypeName": "Future", @@ -669,7 +669,7 @@ "isDeprecated": false, "isExperimental": false, "isStatic": false, - "name": "enable", + "name": "optIn", "parameters": [], "relativePath": "lib/src/posthog_flutter_platform_interface.dart", "returnTypeName": "Future", @@ -1732,7 +1732,7 @@ "isDeprecated": false, "isExperimental": false, "isStatic": false, - "name": "disable", + "name": "optOut", "parameters": [], "relativePath": "lib/src/posthog_flutter_platform_interface.dart", "returnTypeName": "Future", @@ -1744,7 +1744,7 @@ "isDeprecated": false, "isExperimental": false, "isStatic": false, - "name": "enable", + "name": "optIn", "parameters": [], "relativePath": "lib/src/posthog_flutter_platform_interface.dart", "returnTypeName": "Future", diff --git a/posthog_flutter/lib/posthog_flutter_web.dart b/posthog_flutter/lib/posthog_flutter_web.dart index e62c23af..5ef27704 100644 --- a/posthog_flutter/lib/posthog_flutter_web.dart +++ b/posthog_flutter/lib/posthog_flutter_web.dart @@ -347,12 +347,12 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface { } @override - Future disable() async { + Future optOut() async { return handleWebMethodCall(const MethodCall('disable')); } @override - Future enable() async { + Future optIn() async { return handleWebMethodCall(const MethodCall('enable')); } diff --git a/posthog_flutter/lib/src/posthog.dart b/posthog_flutter/lib/src/posthog.dart index 2e315168..2c134fe6 100644 --- a/posthog_flutter/lib/src/posthog.dart +++ b/posthog_flutter/lib/src/posthog.dart @@ -411,7 +411,7 @@ class Posthog { stopReplay = _posthog.stopSessionRecording(); } - await Future.wait([if (stopReplay != null) stopReplay, _posthog.disable()]); + await Future.wait([if (stopReplay != null) stopReplay, _posthog.optOut()]); } /// Opts the current user back in to data collection. @@ -426,7 +426,7 @@ class Posthog { _installFlutterIntegrations(config); } - await _posthog.enable(); + await _posthog.optIn(); final restartReplay = _restartSessionReplayOnEnable; _restartSessionReplayOnEnable = false; if (restartReplay) { diff --git a/posthog_flutter/lib/src/posthog_flutter_io.dart b/posthog_flutter/lib/src/posthog_flutter_io.dart index 386e2a34..eccf7309 100644 --- a/posthog_flutter/lib/src/posthog_flutter_io.dart +++ b/posthog_flutter/lib/src/posthog_flutter_io.dart @@ -512,7 +512,7 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { } @override - Future disable() async { + Future optOut() async { if (!isSupportedPlatform()) { return; } @@ -525,7 +525,7 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { } @override - Future enable() async { + Future optIn() async { if (!isSupportedPlatform()) { return; } diff --git a/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart b/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart index 0e9e7cd2..993ac9a3 100644 --- a/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart +++ b/posthog_flutter/lib/src/posthog_flutter_platform_interface.dart @@ -145,12 +145,12 @@ abstract class PosthogFlutterPlatformInterface extends PlatformInterface { throw UnimplementedError('reset() has not been implemented.'); } - Future disable() { - throw UnimplementedError('disable() has not been implemented.'); + Future optOut() { + throw UnimplementedError('optOut() has not been implemented.'); } - Future enable() { - throw UnimplementedError('enable() has not been implemented.'); + Future optIn() { + throw UnimplementedError('optIn() has not been implemented.'); } Future isOptOut() { diff --git a/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart b/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart index 66e35d79..e4ad6a2b 100644 --- a/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart +++ b/posthog_flutter/test/posthog_flutter_platform_interface_fake.dart @@ -230,10 +230,10 @@ class PosthogFlutterPlatformFake extends PosthogFlutterPlatformInterface { } @override - Future disable() async {} + Future optOut() async {} @override - Future enable() async {} + Future optIn() async {} @override Future close() async { From ef22f6483f6d059ca5c24e1434e8242cfe95a26b Mon Sep 17 00:00:00 2001 From: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:49:16 -0400 Subject: [PATCH 06/12] chore!: remove serialization helpers from the public API (#646) toMap() on the config classes and PostHogFeatureFlagResult.fromMap() are the platform-channel wire format, not a user-facing data shape. Move them to extensions and a top-level function in an unexported src/utils/channel_serialization.dart so they drop out of the public API. Co-authored-by: Claude Opus 5.5 (1M context) --- .changeset/v6-hide-serialization.md | 5 + api/posthog_flutter.api.json | 115 ------------ posthog_flutter/lib/posthog_flutter_web.dart | 3 +- .../lib/src/feature_flag_result.dart | 19 -- posthog_flutter/lib/src/posthog_config.dart | 154 ---------------- .../lib/src/posthog_flutter_io.dart | 3 +- .../lib/src/utils/channel_serialization.dart | 172 ++++++++++++++++++ posthog_flutter/test/posthog_logs_test.dart | 1 + posthog_flutter/test/posthog_test.dart | 11 +- .../test/replay_capture_touches_test.dart | 1 + .../test/replay_screenshot_config_test.dart | 1 + 11 files changed, 190 insertions(+), 295 deletions(-) create mode 100644 .changeset/v6-hide-serialization.md create mode 100644 posthog_flutter/lib/src/utils/channel_serialization.dart diff --git a/.changeset/v6-hide-serialization.md b/.changeset/v6-hide-serialization.md new file mode 100644 index 00000000..9eb46db6 --- /dev/null +++ b/.changeset/v6-hide-serialization.md @@ -0,0 +1,5 @@ +--- +"posthog_flutter": major +--- + +- **Breaking:** remove `toMap()` from the config classes and `PostHogFeatureFlagResult.fromMap()` from the public API — they serialize the internal platform-channel wire format and were never meant for app code diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index 115b5ec9..114d5a2c 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -2322,18 +2322,6 @@ "posthog_flutter.dart" ], "executableDeclarations": [ - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "toMap", - "parameters": [], - "relativePath": "lib/src/posthog_config.dart", - "returnTypeName": "Map", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -2712,18 +2700,6 @@ "posthog_flutter.dart" ], "executableDeclarations": [ - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "toMap", - "parameters": [], - "relativePath": "lib/src/posthog_config.dart", - "returnTypeName": "Map", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -2914,18 +2890,6 @@ "posthog_flutter.dart" ], "executableDeclarations": [ - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "toMap", - "parameters": [], - "relativePath": "lib/src/posthog_config.dart", - "returnTypeName": "Map", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -3226,18 +3190,6 @@ "posthog_flutter.dart" ], "executableDeclarations": [ - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "toMap", - "parameters": [], - "relativePath": "lib/src/posthog_config.dart", - "returnTypeName": "Map", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -3379,18 +3331,6 @@ "posthog_flutter.dart" ], "executableDeclarations": [ - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "toMap", - "parameters": [], - "relativePath": "lib/src/posthog_config.dart", - "returnTypeName": "Map", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -3444,18 +3384,6 @@ "posthog_flutter.dart" ], "executableDeclarations": [ - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "toMap", - "parameters": [], - "relativePath": "lib/src/posthog_config.dart", - "returnTypeName": "Map", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -3608,18 +3536,6 @@ "posthog_flutter.dart" ], "executableDeclarations": [ - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "toMap", - "parameters": [], - "relativePath": "lib/src/posthog_config.dart", - "returnTypeName": "Map", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -3828,37 +3744,6 @@ "posthog_flutter.dart" ], "executableDeclarations": [ - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": true, - "name": "fromMap", - "parameters": [ - { - "isDeprecated": false, - "isExperimental": false, - "isNamed": false, - "isRequired": true, - "name": "result", - "relativePath": "lib/src/feature_flag_result.dart", - "typeName": "Object?" - }, - { - "isDeprecated": false, - "isExperimental": false, - "isNamed": false, - "isRequired": true, - "name": "fallbackKey", - "relativePath": "lib/src/feature_flag_result.dart", - "typeName": "String" - } - ], - "relativePath": "lib/src/feature_flag_result.dart", - "returnTypeName": "PostHogFeatureFlagResult?", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, diff --git a/posthog_flutter/lib/posthog_flutter_web.dart b/posthog_flutter/lib/posthog_flutter_web.dart index 5ef27704..92197986 100644 --- a/posthog_flutter/lib/posthog_flutter_web.dart +++ b/posthog_flutter/lib/posthog_flutter_web.dart @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:posthog_flutter/src/error_tracking/dart_exception_processor.dart'; import 'package:posthog_flutter/src/util/logging.dart'; +import 'package:posthog_flutter/src/utils/channel_serialization.dart'; import 'package:posthog_flutter/src/utils/property_normalizer.dart'; import 'src/feature_flag_result.dart'; @@ -473,7 +474,7 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface { ); // Web SDK returns: { key, enabled, variant, payload } - return PostHogFeatureFlagResult.fromMap(result, key); + return featureFlagResultFromMap(result, key); } @override diff --git a/posthog_flutter/lib/src/feature_flag_result.dart b/posthog_flutter/lib/src/feature_flag_result.dart index 9a85fef5..14298af1 100644 --- a/posthog_flutter/lib/src/feature_flag_result.dart +++ b/posthog_flutter/lib/src/feature_flag_result.dart @@ -53,23 +53,4 @@ class PostHogFeatureFlagResult { @override int get hashCode => Object.hash(key, enabled, variant); - - /// Creates a [PostHogFeatureFlagResult] from a native SDK response map. - /// - /// The [result] should be a map containing `key`, `enabled`, `variant`, and - /// `payload` entries. Falls back to [fallbackKey] if the map does not include - /// a key. - /// - /// Returns `null` if [result] is `null` or is not a [Map]. - static PostHogFeatureFlagResult? fromMap(Object? result, String fallbackKey) { - if (result == null) return null; - if (result is! Map) return null; - - return PostHogFeatureFlagResult( - key: result['key'] as String? ?? fallbackKey, - enabled: result['enabled'] as bool? ?? false, - variant: result['variant'] as String?, - payload: result['payload'], - ); - } } diff --git a/posthog_flutter/lib/src/posthog_config.dart b/posthog_flutter/lib/src/posthog_config.dart index 1b84f7f1..fcbd806f 100644 --- a/posthog_flutter/lib/src/posthog_config.dart +++ b/posthog_flutter/lib/src/posthog_config.dart @@ -7,7 +7,6 @@ import 'posthog.dart'; import 'posthog_event.dart'; import 'posthog_flutter_platform_interface.dart'; import 'replay/mask/posthog_text_mask.dart'; -import 'util/logging.dart'; /// Callback to intercept and modify events before they are sent to PostHog. /// @@ -424,43 +423,6 @@ class PostHogConfig { final trimmedHost = host.trim(); return trimmedHost.isEmpty ? _defaultHost : trimmedHost; } - - /// Converts this configuration to a platform-channel map. - /// - /// Returns the values consumed by the Android, Apple, and web implementations. - Map toMap() { - return { - 'projectToken': projectToken, - 'host': host, - 'flushAt': flushAt, - 'maxQueueSize': maxQueueSize, - 'maxBatchSize': maxBatchSize, - 'flushInterval': flushInterval.inSeconds, - 'sendFeatureFlagEvents': sendFeatureFlagEvent, - 'preloadFeatureFlags': preloadFeatureFlags, - if (evaluationContexts != null) 'evaluationContexts': evaluationContexts, - 'captureApplicationLifecycleEvents': captureApplicationLifecycleEvents, - 'rageClickConfig': rageClickConfig.toMap(), - 'debug': debug, - 'optOut': optOut, - 'surveys': surveys, - 'personProfiles': personProfiles.name, - 'compression': compression.name, - 'sessionReplay': sessionReplay, - 'dataMode': dataMode.name, - 'sessionReplayConfig': sessionReplayConfig.toMap(), - 'errorTrackingConfig': errorTrackingConfig.toMap(), - 'logs': logsConfig.toMap(), - 'capturePushNotificationSubscriptions': - capturePushNotificationSubscriptions, - 'capturePushNotificationOpened': capturePushNotificationOpened, - // A closure can't cross the channel. This tells native whether to install - // a bridging provider at all — installing one the host didn't ask for - // would change how the native SDK handles a 401 on the subscription call. - 'pushIdentityProviderEnabled': pushIdentityProvider != null, - if (bootstrap != null) 'bootstrap': bootstrap!.toMap(), - }; - } } /// Pre-seeded identity and feature-flag state applied on the very first SDK @@ -517,34 +479,6 @@ class PostHogBootstrapConfig { /// JSON payloads paired with [featureFlags], keyed by flag key. Each value is /// the already-decoded payload (map, list, string, number, `null`, ...). final Map? featureFlagPayloads; - - /// Converts this configuration to a platform-channel map. - /// - /// Only the dimensions that were set are included; [isIdentifiedId] is always - /// sent so the native SDK doesn't have to infer it. - Map toMap() { - final flags = featureFlags; - if (flags != null) { - for (final entry in flags.entries) { - // Only bool/String are served (see [featureFlags]); the native SDKs drop - // anything else silently, so warn instead of leaving no trace. - if (entry.value is! bool && entry.value is! String) { - printIfDebug( - '[PostHog] bootstrap featureFlags["${entry.key}"] is ' - '${entry.value.runtimeType}; only bool and String values are served, ' - 'so this entry will be ignored.', - ); - } - } - } - return { - if (distinctId != null) 'distinctId': distinctId, - 'isIdentifiedId': isIdentifiedId, - if (featureFlags != null) 'featureFlags': featureFlags, - if (featureFlagPayloads != null) - 'featureFlagPayloads': featureFlagPayloads, - }; - } } /// Configuration for the logs subsystem. @@ -649,36 +583,6 @@ class PostHogLogsConfig { /// record. Keep anything sensitive out of those fields; put it in [body] or /// [PostHogLogRecord.attributes], which a callback can scrub or drop. List beforeSend = []; - - /// Converts the identity and tuning options to a platform-channel map. - /// - /// Only fields the user set are included, so unset fields keep the native - /// default. [beforeSend] is intentionally omitted: it runs in Dart and never - /// crosses the platform channel. - Map toMap() { - return { - if (serviceName != null) 'serviceName': serviceName, - if (serviceVersion != null) 'serviceVersion': serviceVersion, - if (environment != null) 'environment': environment, - if (resourceAttributes.isNotEmpty) - 'resourceAttributes': resourceAttributes, - if (flushInterval != null) - 'flushIntervalSeconds': _wholeSeconds(flushInterval!), - if (flushAt != null) 'flushAt': flushAt, - if (maxBatchSize != null) 'maxBatchSize': maxBatchSize, - if (maxBufferSize != null) 'maxBufferSize': maxBufferSize, - if (rateCapMaxLogs != null) 'rateCapMaxLogs': rateCapMaxLogs, - if (rateCapWindow != null) - 'rateCapWindowSeconds': _wholeSeconds(rateCapWindow!), - }; - } - - /// The native flush interval and rate-cap window are whole seconds. A - /// sub-second [Duration] truncates to `0`, which the native SDK treats as - /// "disabled" (rate cap) or continuous flushing — surprising for a caller who - /// set, say, 500ms. Floor at 1s, the smallest value the native API can honor. - static int _wholeSeconds(Duration duration) => - duration.inSeconds < 1 ? 1 : duration.inSeconds; } /// Configuration for rage-click autocapture on iOS and Mac Catalyst. @@ -708,17 +612,6 @@ class PostHogRageClickConfig { /// /// Defaults to `3`. var minimumTapCount = 3; - - /// Converts this rage-click configuration to a platform-channel map. - Map toMap() { - return { - 'enabled': enabled, - 'thresholdPoints': thresholdPoints, - 'timeoutInterval': - timeoutInterval.inMicroseconds / Duration.microsecondsPerSecond, - 'minimumTapCount': minimumTapCount, - }; - } } /// Pixel format for Android native-screen captures in session replay. @@ -1000,26 +893,6 @@ class PostHogSessionReplayConfig { /// /// Default: false. var verifyScreenshotMaskAlignment = false; - - /// Converts this session replay configuration to a platform-channel map. - /// - /// Returns values consumed by the Android and Apple session replay - /// implementations. - Map toMap() { - return { - 'captureTouches': captureTouches, - 'maskAllImages': maskAllImages, - 'maskAllTexts': maskAllTexts, - 'throttleDelayMs': throttleDelay.inMilliseconds, - 'maskAllPlatformViews': maskAllPlatformViews, - 'captureNativeScreens': captureNativeScreens, - 'verifyScreenshotMaskAlignment': verifyScreenshotMaskAlignment, - 'screenshotScale': screenshotScale, - 'screenshotCompressionQuality': screenshotCompressionQuality, - 'screenshotColorMode': screenshotColorMode.name, - if (sampleRate != null) 'sampleRate': sampleRate, - }; - } } /// Configuration for PostHog error tracking and exception capture. @@ -1196,25 +1069,6 @@ class PostHogErrorTrackingConfig { /// /// Record steps with `Posthog().addExceptionStep()`. final exceptionSteps = PostHogExceptionStepsConfig(); - - /// Converts this error tracking configuration to a platform-channel map. - /// - /// Returns values consumed by the Android, Apple, and Dart exception capture - /// implementations. - Map toMap() { - return { - 'inAppIncludes': inAppIncludes, - 'inAppExcludes': inAppExcludes, - 'inAppByDefault': inAppByDefault, - 'captureFlutterErrors': captureFlutterErrors, - 'captureSilentFlutterErrors': captureSilentFlutterErrors, - 'capturePlatformDispatcherErrors': capturePlatformDispatcherErrors, - 'captureNativeExceptions': captureNativeExceptions, - 'captureNativeCrashes': captureNativeCrashes, - 'captureIsolateErrors': captureIsolateErrors, - 'exceptionSteps': exceptionSteps.toMap(), - }; - } } /// Configuration for exception steps. @@ -1252,12 +1106,4 @@ class PostHogExceptionStepsConfig { /// until the total fits. A single step larger than the budget is rejected /// outright. Defaults to `32768` (32 KiB). var maxBytes = 32768; - - /// Converts this configuration to a platform-channel map. - Map toMap() { - return { - 'enabled': enabled, - 'maxBytes': maxBytes, - }; - } } diff --git a/posthog_flutter/lib/src/posthog_flutter_io.dart b/posthog_flutter/lib/src/posthog_flutter_io.dart index eccf7309..0c58ced1 100644 --- a/posthog_flutter/lib/src/posthog_flutter_io.dart +++ b/posthog_flutter/lib/src/posthog_flutter_io.dart @@ -12,6 +12,7 @@ import 'surveys/models/survey_callbacks.dart'; import 'error_tracking/dart_exception_processor.dart'; import 'utils/before_send.dart'; import 'utils/capture_utils.dart'; +import 'utils/channel_serialization.dart'; import 'utils/flutter_version.dart'; import 'utils/property_normalizer.dart'; @@ -728,7 +729,7 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { }); // Native returns: { key, enabled, variant, payload } - return PostHogFeatureFlagResult.fromMap(result, key); + return featureFlagResultFromMap(result, key); } on PlatformException catch (exception) { printIfDebug('Exception on getFeatureFlagResult: $exception'); return null; diff --git a/posthog_flutter/lib/src/utils/channel_serialization.dart b/posthog_flutter/lib/src/utils/channel_serialization.dart new file mode 100644 index 00000000..8fc7f2ec --- /dev/null +++ b/posthog_flutter/lib/src/utils/channel_serialization.dart @@ -0,0 +1,172 @@ +import '../feature_flag_result.dart'; +import '../posthog_config.dart'; +import '../util/logging.dart'; + +extension PostHogConfigSerialization on PostHogConfig { + Map toMap() { + return { + 'projectToken': projectToken, + 'host': host, + 'flushAt': flushAt, + 'maxQueueSize': maxQueueSize, + 'maxBatchSize': maxBatchSize, + 'flushInterval': flushInterval.inSeconds, + 'sendFeatureFlagEvents': sendFeatureFlagEvent, + 'preloadFeatureFlags': preloadFeatureFlags, + if (evaluationContexts != null) 'evaluationContexts': evaluationContexts, + 'captureApplicationLifecycleEvents': captureApplicationLifecycleEvents, + 'rageClickConfig': rageClickConfig.toMap(), + 'debug': debug, + 'optOut': optOut, + 'surveys': surveys, + 'personProfiles': personProfiles.name, + 'compression': compression.name, + 'sessionReplay': sessionReplay, + 'dataMode': dataMode.name, + 'sessionReplayConfig': sessionReplayConfig.toMap(), + 'errorTrackingConfig': errorTrackingConfig.toMap(), + 'logs': logsConfig.toMap(), + 'capturePushNotificationSubscriptions': + capturePushNotificationSubscriptions, + 'capturePushNotificationOpened': capturePushNotificationOpened, + // A closure can't cross the channel. This tells native whether to install + // a bridging provider at all — installing one the host didn't ask for + // would change how the native SDK handles a 401 on the subscription call. + 'pushIdentityProviderEnabled': pushIdentityProvider != null, + if (bootstrap != null) 'bootstrap': bootstrap!.toMap(), + }; + } +} + +extension PostHogBootstrapConfigSerialization on PostHogBootstrapConfig { + /// Only the dimensions that were set are included; [isIdentifiedId] is always + /// sent so the native SDK doesn't have to infer it. + Map toMap() { + final flags = featureFlags; + if (flags != null) { + for (final entry in flags.entries) { + // Only bool/String are served (see [featureFlags]); the native SDKs drop + // anything else silently, so warn instead of leaving no trace. + if (entry.value is! bool && entry.value is! String) { + printIfDebug( + '[PostHog] bootstrap featureFlags["${entry.key}"] is ' + '${entry.value.runtimeType}; only bool and String values are served, ' + 'so this entry will be ignored.', + ); + } + } + } + return { + if (distinctId != null) 'distinctId': distinctId, + 'isIdentifiedId': isIdentifiedId, + if (featureFlags != null) 'featureFlags': featureFlags, + if (featureFlagPayloads != null) + 'featureFlagPayloads': featureFlagPayloads, + }; + } +} + +extension PostHogLogsConfigSerialization on PostHogLogsConfig { + /// Only fields the user set are included, so unset fields keep the native + /// default. [beforeSend] is intentionally omitted: it runs in Dart and never + /// crosses the platform channel. + Map toMap() { + return { + if (serviceName != null) 'serviceName': serviceName, + if (serviceVersion != null) 'serviceVersion': serviceVersion, + if (environment != null) 'environment': environment, + if (resourceAttributes.isNotEmpty) + 'resourceAttributes': resourceAttributes, + if (flushInterval != null) + 'flushIntervalSeconds': _wholeSeconds(flushInterval!), + if (flushAt != null) 'flushAt': flushAt, + if (maxBatchSize != null) 'maxBatchSize': maxBatchSize, + if (maxBufferSize != null) 'maxBufferSize': maxBufferSize, + if (rateCapMaxLogs != null) 'rateCapMaxLogs': rateCapMaxLogs, + if (rateCapWindow != null) + 'rateCapWindowSeconds': _wholeSeconds(rateCapWindow!), + }; + } + + /// The native flush interval and rate-cap window are whole seconds. A + /// sub-second [Duration] truncates to `0`, which the native SDK treats as + /// "disabled" (rate cap) or continuous flushing — surprising for a caller who + /// set, say, 500ms. Floor at 1s, the smallest value the native API can honor. + static int _wholeSeconds(Duration duration) => + duration.inSeconds < 1 ? 1 : duration.inSeconds; +} + +extension PostHogRageClickConfigSerialization on PostHogRageClickConfig { + Map toMap() { + return { + 'enabled': enabled, + 'thresholdPoints': thresholdPoints, + 'timeoutInterval': + timeoutInterval.inMicroseconds / Duration.microsecondsPerSecond, + 'minimumTapCount': minimumTapCount, + }; + } +} + +extension PostHogSessionReplayConfigSerialization + on PostHogSessionReplayConfig { + Map toMap() { + return { + 'captureTouches': captureTouches, + 'maskAllImages': maskAllImages, + 'maskAllTexts': maskAllTexts, + 'throttleDelayMs': throttleDelay.inMilliseconds, + 'maskAllPlatformViews': maskAllPlatformViews, + 'captureNativeScreens': captureNativeScreens, + 'verifyScreenshotMaskAlignment': verifyScreenshotMaskAlignment, + 'screenshotScale': screenshotScale, + 'screenshotCompressionQuality': screenshotCompressionQuality, + 'screenshotColorMode': screenshotColorMode.name, + if (sampleRate != null) 'sampleRate': sampleRate, + }; + } +} + +extension PostHogErrorTrackingConfigSerialization + on PostHogErrorTrackingConfig { + Map toMap() { + return { + 'inAppIncludes': inAppIncludes, + 'inAppExcludes': inAppExcludes, + 'inAppByDefault': inAppByDefault, + 'captureFlutterErrors': captureFlutterErrors, + 'captureSilentFlutterErrors': captureSilentFlutterErrors, + 'capturePlatformDispatcherErrors': capturePlatformDispatcherErrors, + 'captureNativeExceptions': captureNativeExceptions, + 'captureNativeCrashes': captureNativeCrashes, + 'captureIsolateErrors': captureIsolateErrors, + 'exceptionSteps': exceptionSteps.toMap(), + }; + } +} + +extension PostHogExceptionStepsConfigSerialization + on PostHogExceptionStepsConfig { + Map toMap() { + return { + 'enabled': enabled, + 'maxBytes': maxBytes, + }; + } +} + +/// Returns `null` if [result] is not a [Map]. Falls back to [fallbackKey] when +/// the map has no `key`. +PostHogFeatureFlagResult? featureFlagResultFromMap( + Object? result, + String fallbackKey, +) { + if (result is! Map) return null; + + return PostHogFeatureFlagResult( + key: result['key'] as String? ?? fallbackKey, + enabled: result['enabled'] as bool? ?? false, + variant: result['variant'] as String?, + payload: result['payload'], + ); +} diff --git a/posthog_flutter/test/posthog_logs_test.dart b/posthog_flutter/test/posthog_logs_test.dart index 8d0d46f3..624822a8 100644 --- a/posthog_flutter/test/posthog_logs_test.dart +++ b/posthog_flutter/test/posthog_logs_test.dart @@ -1,6 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:posthog_flutter/posthog_flutter.dart'; import 'package:posthog_flutter/src/posthog_flutter_platform_interface.dart'; +import 'package:posthog_flutter/src/utils/channel_serialization.dart'; import 'posthog_flutter_platform_interface_fake.dart'; diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index 84f138a3..4ddb2fa8 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -8,6 +8,7 @@ import 'package:posthog_flutter/src/posthog_flutter_platform_interface.dart'; import 'package:posthog_flutter/src/posthog_internal_events.dart'; import 'package:posthog_flutter/src/replay/mask/posthog_mask_controller.dart'; import 'package:posthog_flutter/src/replay/screenshot/screenshot_capturer.dart'; +import 'package:posthog_flutter/src/utils/channel_serialization.dart'; import 'posthog_flutter_platform_interface_fake.dart'; @@ -617,12 +618,12 @@ void main() { }); test('fromMap returns null for null input', () { - final result = PostHogFeatureFlagResult.fromMap(null, 'fallback'); + final result = featureFlagResultFromMap(null, 'fallback'); expect(result, isNull); }); test('fromMap returns null for non-Map input', () { - final result = PostHogFeatureFlagResult.fromMap('not a map', 'fallback'); + final result = featureFlagResultFromMap('not a map', 'fallback'); expect(result, isNull); }); @@ -634,7 +635,7 @@ void main() { 'payload': {'data': 123}, }; - final result = PostHogFeatureFlagResult.fromMap(map, 'fallback'); + final result = featureFlagResultFromMap(map, 'fallback'); expect(result, isNotNull); expect(result!.key, equals('my-flag')); @@ -646,7 +647,7 @@ void main() { test('fromMap uses fallback key when map key is null', () { final map = {'enabled': true, 'variant': null, 'payload': null}; - final result = PostHogFeatureFlagResult.fromMap(map, 'fallback-key'); + final result = featureFlagResultFromMap(map, 'fallback-key'); expect(result, isNotNull); expect(result!.key, equals('fallback-key')); @@ -655,7 +656,7 @@ void main() { test('fromMap defaults enabled to false when not in map', () { final map = {'key': 'my-flag'}; - final result = PostHogFeatureFlagResult.fromMap(map, 'fallback'); + final result = featureFlagResultFromMap(map, 'fallback'); expect(result, isNotNull); expect(result!.enabled, isFalse); diff --git a/posthog_flutter/test/replay_capture_touches_test.dart b/posthog_flutter/test/replay_capture_touches_test.dart index 485c5cb2..1a4f9d1d 100644 --- a/posthog_flutter/test/replay_capture_touches_test.dart +++ b/posthog_flutter/test/replay_capture_touches_test.dart @@ -4,6 +4,7 @@ import 'package:posthog_flutter/posthog_flutter.dart'; import 'package:posthog_flutter/src/posthog_flutter_io.dart'; import 'package:posthog_flutter/src/posthog_flutter_platform_interface.dart'; import 'package:posthog_flutter/src/posthog_internal_events.dart'; +import 'package:posthog_flutter/src/utils/channel_serialization.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/posthog_flutter/test/replay_screenshot_config_test.dart b/posthog_flutter/test/replay_screenshot_config_test.dart index edc745a2..d562fd4f 100644 --- a/posthog_flutter/test/replay_screenshot_config_test.dart +++ b/posthog_flutter/test/replay_screenshot_config_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:posthog_flutter/posthog_flutter.dart'; +import 'package:posthog_flutter/src/utils/channel_serialization.dart'; void main() { test('screenshot controls preserve native defaults', () { From f6fecfc385d6ce89df5e363e3078e56247b23053 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Wed, 7 Oct 2026 16:04:08 -0400 Subject: [PATCH 07/12] chore!: require iOS 15 and macOS 11 Raises the deployment targets to the posthog-ios 4.0 minimums ahead of the SDK bump, so the bump itself only changes the dependency range. Drops the availability checks that are now always true and moves the CodeQL Swift job to Xcode 26. Co-Authored-By: Claude Opus 5.5 (1M context) --- .changeset/v6-remove-deprecated-apis.md | 1 + .github/workflows/codeql.yml | 4 ++-- example/ios/Podfile | 2 +- example/ios/Podfile.lock | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 18 +++++++++--------- example/macos/Podfile | 2 +- example/macos/Podfile.lock | 2 +- example/macos/Runner.xcodeproj/project.pbxproj | 6 +++--- posthog_flutter/darwin/posthog_flutter.podspec | 6 ++---- .../darwin/posthog_flutter/Package.swift | 4 ++-- .../posthog_flutter/PosthogFlutterPlugin.swift | 16 ++++++---------- 11 files changed, 29 insertions(+), 34 deletions(-) diff --git a/.changeset/v6-remove-deprecated-apis.md b/.changeset/v6-remove-deprecated-apis.md index 52ef0668..566af215 100644 --- a/.changeset/v6-remove-deprecated-apis.md +++ b/.changeset/v6-remove-deprecated-apis.md @@ -2,6 +2,7 @@ "posthog_flutter": major --- +- **Breaking:** bump minimum iOS deployment target from 13.0 to 15.0 and macOS from 10.15 to 11.0 - **Breaking:** remove `PostHogConfig.apiKey` and the `com.posthog.posthog.API_KEY` manifest/Info.plist key — use `projectToken` and `com.posthog.posthog.PROJECT_TOKEN` - **Breaking:** remove `Posthog().getFeatureFlagPayload()` — use `getFeatureFlagResult(key, sendFeatureFlagEvent: false)` and read `payload` - **Breaking:** remove the `PostHogSessionReplayConfig.debouncerDelay` setter — use `PostHogSessionReplayConfig.throttleDelay` diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7cd6d889..7cbe95dd 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: runner: ubuntu-latest - language: swift build-mode: manual - runner: macos-15-xlarge + runner: macos-26 - language: actions build-mode: none runner: ubuntu-latest @@ -45,7 +45,7 @@ jobs: if: matrix.language == 'swift' uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0 with: - xcode-version: '16.4' + xcode-version: '26.4' - name: Install dependencies (Swift) if: matrix.language == 'swift' diff --git a/example/ios/Podfile b/example/ios/Podfile index 266a1cd0..e20ea2b7 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '14.0' +platform :ios, '15.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 6356c2c8..70f8356f 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -33,6 +33,6 @@ SPEC CHECKSUMS: google_maps_flutter_ios: 17552876e72723da1d41accc22f03b5f7afbde69 GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d -PODFILE CHECKSUM: 3c864ae2f1db9f17d3418f22aa4b78eac050c10b +PODFILE CHECKSUM: 583e7c5826ead1663c2849ec27bee3d398ceebac COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index b6975b16..535d541f 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -478,7 +478,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -497,7 +497,7 @@ DEVELOPMENT_TEAM = PNC2XCH2XP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -518,7 +518,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.example.posthogFlutterExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -537,7 +537,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.example.posthogFlutterExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -554,7 +554,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.example.posthogFlutterExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -610,7 +610,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -659,7 +659,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -680,7 +680,7 @@ DEVELOPMENT_TEAM = PNC2XCH2XP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -704,7 +704,7 @@ DEVELOPMENT_TEAM = PNC2XCH2XP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/example/macos/Podfile b/example/macos/Podfile index b52666a1..dbccf89c 100644 --- a/example/macos/Podfile +++ b/example/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.15' +platform :osx, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 9d359e12..36151c7c 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -11,6 +11,6 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 -PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3 +PODFILE CHECKSUM: c2e95c8c0fe03c5c57e438583cae4cc732296009 COCOAPODS: 1.16.2 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index f6c36aa8..c0dda7a1 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -549,7 +549,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -628,7 +628,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -675,7 +675,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/posthog_flutter/darwin/posthog_flutter.podspec b/posthog_flutter/darwin/posthog_flutter.podspec index c39b30b2..98e3a7cf 100644 --- a/posthog_flutter/darwin/posthog_flutter.podspec +++ b/posthog_flutter/darwin/posthog_flutter.podspec @@ -21,12 +21,10 @@ Postog flutter plugin s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' - # ~> Version 3.80.0 up to, but not including, 4.0.0 s.dependency 'PostHog', '>= 3.84.0', '< 4.0.0' - s.ios.deployment_target = '13.0' - # PH iOS SDK 3.0.0 requires >= 10.15 - s.osx.deployment_target = '10.15' + s.ios.deployment_target = '15.0' + s.osx.deployment_target = '11.0' # Flutter.framework does not contain a i386 slice. s.ios.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/posthog_flutter/darwin/posthog_flutter/Package.swift b/posthog_flutter/darwin/posthog_flutter/Package.swift index dd9a201d..1cf4470c 100644 --- a/posthog_flutter/darwin/posthog_flutter/Package.swift +++ b/posthog_flutter/darwin/posthog_flutter/Package.swift @@ -6,8 +6,8 @@ import PackageDescription let package = Package( name: "posthog_flutter", platforms: [ - .iOS("13.0"), - .macOS("10.15"), + .iOS("15.0"), + .macOS("11.0"), ], products: [ .library(name: "posthog-flutter", targets: ["posthog_flutter"]), 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 b60b392d..1fa099c4 100644 --- a/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift +++ b/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift @@ -104,9 +104,7 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin { /// a later `FlutterEngine` registers — that setup() has already run, so use the plist key there. private static func prewarmPushNotificationOpenCapture() { guard plistCapturePushNotificationOpened else { return } - if #available(iOS 14.0, macOS 11.0, *) { - PostHogSDK.prewarmPushNotificationOpenCapture() - } + PostHogSDK.prewarmPushNotificationOpenCapture() } private static var plistCapturePushNotificationOpened: Bool { @@ -301,13 +299,11 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin { } // configure surveys - if #available(iOS 15.0, *) { - let surveys: Bool = posthogConfig["surveys"] as? Bool ?? false - config.surveys = surveys - if surveys { - // if surveys are enabled, assign this instance as the survey delegate (we'll take over rendering) - config.surveysConfig.surveysDelegate = instance - } + let surveys: Bool = posthogConfig["surveys"] as? Bool ?? false + config.surveys = surveys + if surveys { + // if surveys are enabled, assign this instance as the survey delegate (we'll take over rendering) + config.surveysConfig.surveysDelegate = instance } #endif From cf0b791dc94ebfbd7162f063ab8bf62f8027886f Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Fri, 23 Oct 2026 11:46:03 -0400 Subject: [PATCH 08/12] chore: move the desktop platform onto the v6 API names Desktop landed on main after the v6 removals were written, so it still implemented disable/enable, getFeatureFlagPayload and the sendEvent parameter, and read config.sendFeatureFlagEvents. --- .../lib/src/posthog_flutter_desktop.dart | 19 +++---- .../test/posthog_desktop_transport_test.dart | 8 +-- .../test/posthog_flutter_dart_test.dart | 2 +- .../test/posthog_flutter_desktop_test.dart | 57 +++++++++++-------- 4 files changed, 45 insertions(+), 41 deletions(-) diff --git a/posthog_flutter/lib/src/posthog_flutter_desktop.dart b/posthog_flutter/lib/src/posthog_flutter_desktop.dart index b69255ee..f8baec36 100644 --- a/posthog_flutter/lib/src/posthog_flutter_desktop.dart +++ b/posthog_flutter/lib/src/posthog_flutter_desktop.dart @@ -411,13 +411,13 @@ class PosthogFlutterDesktop extends PosthogFlutterPlatformInterface { Future reset() => _guard('reset', (client) => client.reset()); @override - Future disable() => _guard('disable', (client) { + Future optOut() => _guard('optOut', (client) { client.optOut(); _appLifecycle?.stop(); }); @override - Future enable() => _guard('enable', (client) { + Future optIn() => _guard('optIn', (client) { client.optIn(); // Opting in is not an app open: an active app is reported as opened // on its next activation. @@ -509,19 +509,16 @@ class PosthogFlutterDesktop extends PosthogFlutterPlatformInterface { (client) => client.getFeatureFlag(key, sendEvent: sendFeatureFlagEvent)); - @override - Future getFeatureFlagPayload({required String key}) => _guardWith( - 'getFeatureFlagPayload', - null, - (client) => client.getFeatureFlagResult(key, sendEvent: false)?.payload); - @override Future getFeatureFlagResult({ required String key, - bool sendEvent = true, + bool sendFeatureFlagEvent = true, }) => - _guardWith('getFeatureFlagResult', null, - (client) => client.getFeatureFlagResult(key, sendEvent: sendEvent)); + _guardWith( + 'getFeatureFlagResult', + null, + (client) => client.getFeatureFlagResult(key, + sendEvent: sendFeatureFlagEvent)); @override Future?> getAllFeatureFlags() => _guardWith( diff --git a/posthog_flutter/test/posthog_desktop_transport_test.dart b/posthog_flutter/test/posthog_desktop_transport_test.dart index 2c8dbfbd..c36cfae2 100644 --- a/posthog_flutter/test/posthog_desktop_transport_test.dart +++ b/posthog_flutter/test/posthog_desktop_transport_test.dart @@ -58,10 +58,10 @@ void main() { ); await IOOverrides.runZoned(() async { await Posthog().setup(config()..preloadFeatureFlags = false); - await Posthog().disable(); + await Posthog().optOut(); expect(await Posthog().isOptOut(), isTrue); await Posthog().capture(eventName: 'blocked event'); - await Posthog().enable(); + await Posthog().optIn(); await Posthog().capture(eventName: 'memory event'); await Posthog().flush(); expect(server.eventNames, ['memory event']); @@ -118,10 +118,10 @@ void main() { expect(personProperties[r'$device_type'], 'Desktop'); expect(personProperties[r'$app_version'], '1.2.3'); - await Posthog().disable(); + await Posthog().optOut(); expect(await Posthog().isOptOut(), isTrue); await Posthog().capture(eventName: 'blocked event'); - await Posthog().enable(); + await Posthog().optIn(); await Posthog().capture(eventName: 'allowed event'); await server.waitForEvent('allowed event'); diff --git a/posthog_flutter/test/posthog_flutter_dart_test.dart b/posthog_flutter/test/posthog_flutter_dart_test.dart index 4f7a8698..1883d664 100644 --- a/posthog_flutter/test/posthog_flutter_dart_test.dart +++ b/posthog_flutter/test/posthog_flutter_dart_test.dart @@ -78,7 +78,7 @@ void main() { PosthogFlutterDart.registerWith(); var platform = PosthogFlutterPlatformInterface.instance; await platform.setup(configFor(server)); - await platform.disable(); + await platform.optOut(); await platform.close(); PosthogFlutterDart.registerWith(); diff --git a/posthog_flutter/test/posthog_flutter_desktop_test.dart b/posthog_flutter/test/posthog_flutter_desktop_test.dart index 9f404c1f..c2f51696 100644 --- a/posthog_flutter/test/posthog_flutter_desktop_test.dart +++ b/posthog_flutter/test/posthog_flutter_desktop_test.dart @@ -89,8 +89,6 @@ void main() { final nullReadsByName = Function(PosthogFlutterDesktop)>{ 'getFeatureFlag': (platform) => platform.getFeatureFlag(key: 'flag'), - 'getFeatureFlagPayload': (platform) => - platform.getFeatureFlagPayload(key: 'flag'), 'getFeatureFlagResult': (platform) => platform.getFeatureFlagResult(key: 'flag'), 'getAllFeatureFlags': (platform) => platform.getAllFeatureFlags(), @@ -133,7 +131,7 @@ void main() { await platform.setup(config); await platform.capture(eventName: 'disabled event'); await platform.identify(userId: 'disabled user'); - await platform.enable(); + await platform.optIn(); await platform.reloadFeatureFlags(); await platform.flush(); expect(await platform.getFeatureFlag(key: 'flag'), isNull); @@ -247,9 +245,9 @@ void main() { ]; final platform = await setUpPlatform(config); - await platform.disable(); + await platform.optOut(); final blocked = platform.capture(eventName: 'blocked capture'); - await platform.enable(); + await platform.optIn(); releaseHook.complete(); await blocked; await platform.capture(eventName: 'sentinel event'); @@ -345,9 +343,9 @@ void main() { ]; final platform = await setUpPlatform(config); - await platform.disable(); + await platform.optOut(); await platform.screen(screenName: 'Hidden'); - await platform.enable(); + await platform.optIn(); await platform.capture(eventName: 'sentinel event'); final event = await server.waitForEvent('sentinel event'); @@ -377,10 +375,10 @@ void main() { final screen = platform.screen(screenName: 'Blocked'); await hookStarted.future; - await platform.disable(); + await platform.optOut(); releaseHook.complete(); await screen; - await platform.enable(); + await platform.optIn(); await platform.capture(eventName: 'sentinel event'); final event = await server.waitForEvent('sentinel event'); @@ -677,9 +675,9 @@ void main() { ]; final platform = await setUpPlatform(config); - await platform.disable(); + await platform.optOut(); final blocked = platform.captureException(error: StateError('blocked')); - await platform.enable(); + await platform.optIn(); releaseHook.complete(); await blocked; await platform.capture(eventName: 'sentinel event'); @@ -918,9 +916,9 @@ void main() { final server = await LocalPostHogServer.start(); final platform = await setUpPlatform(configFor(server)); - await platform.disable(); + await platform.optOut(); await platform.addExceptionStep('while opted out'); - await platform.enable(); + await platform.optIn(); await platform.captureException(error: StateError('opted in')); final event = await server.waitForEvent(r'$exception'); @@ -1053,7 +1051,7 @@ void main() { final platform = launch(createAppDirectory()); await platform.setup(configFor(server)..optOut = true); - await platform.enable(); + await platform.optIn(); await server.waitForEvent('Application Installed'); }); @@ -1092,7 +1090,7 @@ void main() { final firstLaunch = createPlatform(appDirectory: appDirectory); await firstLaunch.setup(configFor(server)); final distinctId = await firstLaunch.getDistinctId(); - await firstLaunch.disable(); + await firstLaunch.optOut(); await firstLaunch.close(); final nextLaunch = createPlatform(appDirectory: appDirectory); @@ -1133,12 +1131,12 @@ void main() { final platform = createPlatform(resolveAppDirectory: () => release.future); final setup = platform.setup(configFor(server)); - final disable = platform.disable(); + final disable = platform.optOut(); final capture = platform.capture(eventName: 'disabled event'); release.complete(directory.path); await Future.wait([setup, disable, capture]); expect(await platform.isOptOut(), isTrue); - await platform.enable(); + await platform.optIn(); await platform.capture(eventName: 'enabled event'); await server.waitForEvent('enabled event'); expect(server.eventNames, ['enabled event']); @@ -1149,7 +1147,7 @@ void main() { final directory = createAppDirectory(); final previousLaunch = createPlatform(appDirectory: directory); await previousLaunch.setup(configFor(server)); - await previousLaunch.disable(); + await previousLaunch.optOut(); await previousLaunch.close(); final release = Completer(); @@ -1160,7 +1158,7 @@ void main() { release.complete(directory.path); await Future.wait([setup, capture]); expect(await platform.isOptOut(), isTrue); - await platform.enable(); + await platform.optIn(); await platform.capture(eventName: 'enabled event'); await server.waitForEvent('enabled event'); expect(server.eventNames, ['enabled event']); @@ -1273,7 +1271,7 @@ void main() { final platform = createPlatform(); final setup = platform.setup(configFor(server)); - final disable = platform.disable(); + final disable = platform.optOut(); await Future.wait([setup, disable]); expect(await platform.isOptOut(), isTrue); @@ -1647,7 +1645,11 @@ void main() { expect(await platform.getFeatureFlag(key: 'beta'), isTrue); expect(await platform.getFeatureFlag(key: 'checkout'), 'variant-b'); expect( - await platform.getFeatureFlagPayload(key: 'beta'), + (await platform.getFeatureFlagResult( + key: 'beta', + sendFeatureFlagEvent: false, + )) + ?.payload, {'color': 'blue'}, ); expect(server.flagsRequests, isEmpty); @@ -1719,7 +1721,8 @@ void main() { ); }); - test(r'getFeatureFlagPayload does not capture $feature_flag_called', + test( + r'getFeatureFlagResult with sendFeatureFlagEvent false does not capture $feature_flag_called', () async { final server = await LocalPostHogServer.start(); server.respond = (_) => PostHogResponse.json(_variantFlagResponse()); @@ -1731,10 +1734,11 @@ void main() { final platform = await setUpPlatform(config); await flagsLoaded.future; - final payload = await platform.getFeatureFlagPayload( + final silent = await platform.getFeatureFlagResult( key: 'variant-flag', + sendFeatureFlagEvent: false, ); - expect(payload, {'color': 'blue'}); + expect(silent?.payload, {'color': 'blue'}); await platform.capture(eventName: 'sentinel event'); await server.waitForEvent('sentinel event'); @@ -1904,7 +1908,10 @@ void main() { await platform.setup(configFor(server) ..preloadFeatureFlags = false ..onFeatureFlags = () { - payloads.add(platform.getFeatureFlagPayload(key: 'variant-flag')); + payloads.add(platform + .getFeatureFlagResult( + key: 'variant-flag', sendFeatureFlagEvent: false) + .then((result) => result?.payload)); if (!announced.isCompleted) announced.complete(); }); await announced.future; From 713e9733b0cbf69bdc77e7226aa6aef1198004c6 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Mon, 26 Oct 2026 14:35:27 -0400 Subject: [PATCH 09/12] chore: apply the v6 API names to code added on main getAllFeatureFlags (#665) decoded with PostHogFeatureFlagResult.fromMap, which #646 made private, and the web feature events (#667) passed sendEvent:, which #647 renamed to sendFeatureFlagEvent:. Co-Authored-By: Claude Opus 5.5 --- api/posthog_flutter.api.json | 99 ------------------- posthog_flutter/lib/posthog_flutter_web.dart | 4 +- .../lib/src/posthog_flutter_io.dart | 2 +- 3 files changed, 3 insertions(+), 102 deletions(-) diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index 114d5a2c..d3767214 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -986,28 +986,6 @@ "type": "method", "typeParameterNames": [] }, - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "getFeatureFlagPayload", - "parameters": [ - { - "isDeprecated": false, - "isExperimental": false, - "isNamed": true, - "isRequired": true, - "name": "key", - "relativePath": "lib/src/posthog_flutter_platform_interface.dart", - "typeName": "String" - } - ], - "relativePath": "lib/src/posthog_flutter_platform_interface.dart", - "returnTypeName": "Future", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -2061,28 +2039,6 @@ "type": "method", "typeParameterNames": [] }, - { - "entryPoints": [], - "isDeprecated": false, - "isExperimental": false, - "isStatic": false, - "name": "getFeatureFlagPayload", - "parameters": [ - { - "isDeprecated": false, - "isExperimental": false, - "isNamed": true, - "isRequired": true, - "name": "key", - "relativePath": "lib/src/posthog_flutter_platform_interface.dart", - "typeName": "String" - } - ], - "relativePath": "lib/src/posthog_flutter_platform_interface.dart", - "returnTypeName": "Future", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, @@ -2375,17 +2331,6 @@ "relativePath": "lib/src/posthog_config.dart", "typeName": "String" }, - { - "entryPoints": [], - "isDeprecated": true, - "isExperimental": false, - "isReadable": true, - "isStatic": false, - "isWriteable": false, - "name": "apiKey", - "relativePath": "lib/src/posthog_config.dart", - "typeName": "String" - }, { "entryPoints": [], "isDeprecated": false, @@ -2959,17 +2904,6 @@ "relativePath": "lib/src/posthog_config.dart", "typeName": "PostHogTextMask Function(String, Widget)?" }, - { - "entryPoints": [], - "isDeprecated": true, - "isExperimental": false, - "isReadable": false, - "isStatic": false, - "isWriteable": true, - "name": "debouncerDelay", - "relativePath": "lib/src/posthog_config.dart", - "typeName": "Duration" - }, { "entryPoints": [], "isDeprecated": false, @@ -3139,17 +3073,6 @@ "relativePath": "lib/src/posthog_config.dart", "typeName": "PostHogDataMode" }, - { - "entryPoints": [], - "isDeprecated": true, - "isExperimental": false, - "isReadable": true, - "isStatic": true, - "isWriteable": false, - "name": "cellular", - "relativePath": "lib/src/posthog_config.dart", - "typeName": "PostHogDataMode" - }, { "entryPoints": [], "isDeprecated": false, @@ -4942,28 +4865,6 @@ "type": "method", "typeParameterNames": [] }, - { - "entryPoints": [], - "isDeprecated": true, - "isExperimental": false, - "isStatic": false, - "name": "getFeatureFlagPayload", - "parameters": [ - { - "isDeprecated": false, - "isExperimental": false, - "isNamed": false, - "isRequired": true, - "name": "key", - "relativePath": "lib/src/posthog.dart", - "typeName": "String" - } - ], - "relativePath": "lib/src/posthog.dart", - "returnTypeName": "Future", - "type": "method", - "typeParameterNames": [] - }, { "entryPoints": [], "isDeprecated": false, diff --git a/posthog_flutter/lib/posthog_flutter_web.dart b/posthog_flutter/lib/posthog_flutter_web.dart index 92197986..88047d82 100644 --- a/posthog_flutter/lib/posthog_flutter_web.dart +++ b/posthog_flutter/lib/posthog_flutter_web.dart @@ -327,7 +327,7 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface { ) async { final screenName = _currentScreen; final result = flagVariant == null - ? await getFeatureFlagResult(key: flag, sendEvent: false) + ? await getFeatureFlagResult(key: flag, sendFeatureFlagEvent: false) : null; final captured = featureEvent( event, flag, flagVariant ?? result?.variant ?? result?.enabled); @@ -483,7 +483,7 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface { await handleWebMethodCall(const MethodCall('getAllFeatureFlags')); if (results is! List) return null; return results - .map((result) => PostHogFeatureFlagResult.fromMap(result, '')) + .map((result) => featureFlagResultFromMap(result, '')) .nonNulls .toList(); } diff --git a/posthog_flutter/lib/src/posthog_flutter_io.dart b/posthog_flutter/lib/src/posthog_flutter_io.dart index 0c58ced1..f5a1b2ad 100644 --- a/posthog_flutter/lib/src/posthog_flutter_io.dart +++ b/posthog_flutter/lib/src/posthog_flutter_io.dart @@ -746,7 +746,7 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface { final results = await _methodChannel .invokeMethod>('getAllFeatureFlags'); return results - ?.map((result) => PostHogFeatureFlagResult.fromMap(result, '')) + ?.map((result) => featureFlagResultFromMap(result, '')) .nonNulls .toList(); } on PlatformException catch (exception) { From ef96460bde0c1cf3c189bf0b36f6b35016462406 Mon Sep 17 00:00:00 2001 From: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com> Date: Tue, 27 Oct 2026 09:56:51 -0400 Subject: [PATCH 10/12] chore!: make getFeatureFlagResult follow config.sendFeatureFlagEvent by default (#662) 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, ); }); From 96370b265116caf09e0dc3cccd7b2cb7910bf740 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Fri, 30 Oct 2026 18:50:05 -0400 Subject: [PATCH 11/12] docs: consolidate 6.0 changeset and migration guide --- .changeset/v6-flag-result-follows-config.md | 5 - .changeset/v6-hide-serialization.md | 5 - .changeset/v6-major-release.md | 16 ++ .changeset/v6-platform-opt-in-out.md | 5 - .changeset/v6-remove-5x-deprecations.md | 6 - .changeset/v6-remove-deprecated-apis.md | 10 - .../v6-send-feature-flag-event-param.md | 5 - posthog_flutter/MIGRATION.md | 200 ++++++++++++++++++ 8 files changed, 216 insertions(+), 36 deletions(-) delete mode 100644 .changeset/v6-flag-result-follows-config.md delete mode 100644 .changeset/v6-hide-serialization.md create mode 100644 .changeset/v6-major-release.md delete mode 100644 .changeset/v6-platform-opt-in-out.md delete mode 100644 .changeset/v6-remove-5x-deprecations.md delete mode 100644 .changeset/v6-remove-deprecated-apis.md delete mode 100644 .changeset/v6-send-feature-flag-event-param.md create mode 100644 posthog_flutter/MIGRATION.md diff --git a/.changeset/v6-flag-result-follows-config.md b/.changeset/v6-flag-result-follows-config.md deleted file mode 100644 index c9b87b6c..00000000 --- a/.changeset/v6-flag-result-follows-config.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"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/.changeset/v6-hide-serialization.md b/.changeset/v6-hide-serialization.md deleted file mode 100644 index 9eb46db6..00000000 --- a/.changeset/v6-hide-serialization.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"posthog_flutter": major ---- - -- **Breaking:** remove `toMap()` from the config classes and `PostHogFeatureFlagResult.fromMap()` from the public API — they serialize the internal platform-channel wire format and were never meant for app code diff --git a/.changeset/v6-major-release.md b/.changeset/v6-major-release.md new file mode 100644 index 00000000..51a38c5c --- /dev/null +++ b/.changeset/v6-major-release.md @@ -0,0 +1,16 @@ +--- +"posthog_flutter": major +--- + +- **Breaking:** bump the minimum iOS deployment target from 13.0 to 15.0 and macOS from 10.15 to 11.0 — see the [migration guide](https://github.com/PostHog/posthog-flutter/blob/main/posthog_flutter/MIGRATION.md) +- **Breaking:** change `getFeatureFlagResult()` to follow `PostHogConfig.sendFeatureFlagEvent` on iOS, macOS, Android, Windows and Linux when `sendFeatureFlagEvent` is omitted — pass `sendFeatureFlagEvent: true` to keep capturing `$feature_flag_called` +- **Breaking:** rename `getFeatureFlagResult(sendEvent:)` to `getFeatureFlagResult(sendFeatureFlagEvent:)` +- **Breaking:** remove `PostHogConfig.apiKey` and the `com.posthog.posthog.API_KEY` manifest and Info.plist key — use `projectToken` and `com.posthog.posthog.PROJECT_TOKEN` +- **Breaking:** remove `Posthog().enable()` and `Posthog().disable()` — use `optIn()` and `optOut()` +- **Breaking:** remove `PostHogConfig.sendFeatureFlagEvents` — use `sendFeatureFlagEvent` +- **Breaking:** remove `Posthog().getFeatureFlagPayload()` — use `getFeatureFlagResult(key, sendFeatureFlagEvent: false)` and read `payload` +- **Breaking:** remove the `PostHogSessionReplayConfig.debouncerDelay` setter — use `throttleDelay` +- **Breaking:** remove `PostHogDataMode.cellular` — use `PostHogDataMode.any` +- **Breaking:** remove `toMap()` from the config classes and `PostHogFeatureFlagResult.fromMap()` from the public API +- **Breaking:** rename `enable()` and `disable()` to `optIn()` and `optOut()` on `PosthogFlutterPlatformInterface`, `PosthogFlutterIO` and `PosthogFlutterWeb` +- Declare Android `minSdkVersion` 23 to match `posthog-android` diff --git a/.changeset/v6-platform-opt-in-out.md b/.changeset/v6-platform-opt-in-out.md deleted file mode 100644 index 0bdcaddf..00000000 --- a/.changeset/v6-platform-opt-in-out.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"posthog_flutter": major ---- - -- **Breaking:** rename `enable()`/`disable()` to `optIn()`/`optOut()` on `PosthogFlutterPlatformInterface`, `PosthogFlutterIO` and `PosthogFlutterWeb`. Update direct calls to these platform classes, custom implementations, and test fakes/mocks. Calls to `Posthog().optIn()`/`optOut()` are unchanged diff --git a/.changeset/v6-remove-5x-deprecations.md b/.changeset/v6-remove-5x-deprecations.md deleted file mode 100644 index 7cde3baa..00000000 --- a/.changeset/v6-remove-5x-deprecations.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"posthog_flutter": major ---- - -- **Breaking:** remove `Posthog().enable()` and `Posthog().disable()` — use `optIn()` and `optOut()` -- **Breaking:** remove `PostHogConfig.sendFeatureFlagEvents` — use `sendFeatureFlagEvent` diff --git a/.changeset/v6-remove-deprecated-apis.md b/.changeset/v6-remove-deprecated-apis.md deleted file mode 100644 index 566af215..00000000 --- a/.changeset/v6-remove-deprecated-apis.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"posthog_flutter": major ---- - -- **Breaking:** bump minimum iOS deployment target from 13.0 to 15.0 and macOS from 10.15 to 11.0 -- **Breaking:** remove `PostHogConfig.apiKey` and the `com.posthog.posthog.API_KEY` manifest/Info.plist key — use `projectToken` and `com.posthog.posthog.PROJECT_TOKEN` -- **Breaking:** remove `Posthog().getFeatureFlagPayload()` — use `getFeatureFlagResult(key, sendFeatureFlagEvent: false)` and read `payload` -- **Breaking:** remove the `PostHogSessionReplayConfig.debouncerDelay` setter — use `PostHogSessionReplayConfig.throttleDelay` -- **Breaking:** remove `PostHogDataMode.cellular` — use `PostHogDataMode.any`, which it always behaved like -- Declare Android `minSdkVersion` 23 to match `posthog-android`, which has required it since 3.39.0. Apps on minSdk 21 or 22 already could not build with this plugin, so nothing changes for them diff --git a/.changeset/v6-send-feature-flag-event-param.md b/.changeset/v6-send-feature-flag-event-param.md deleted file mode 100644 index 677d86fb..00000000 --- a/.changeset/v6-send-feature-flag-event-param.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"posthog_flutter": major ---- - -- **Breaking:** `getFeatureFlagResult(sendEvent:)` is now `getFeatureFlagResult(sendFeatureFlagEvent:)`, matching `PostHogConfig.sendFeatureFlagEvent` and the native SDKs diff --git a/posthog_flutter/MIGRATION.md b/posthog_flutter/MIGRATION.md new file mode 100644 index 00000000..cd8cb887 --- /dev/null +++ b/posthog_flutter/MIGRATION.md @@ -0,0 +1,200 @@ +# Migrating from posthog_flutter 5.x to 6.0 + +## Requirements + +| | 5.x | 6.0 | +| --- | --- | --- | +| iOS deployment target | 13.0 | 15.0 | +| macOS deployment target | 10.15 | 11.0 | +| Android `minSdkVersion` declared by the plugin | 21 | 23 | + +The Dart (`>=3.6.0`) and Flutter (`>=3.27.0`) minimums are unchanged. + +### iOS and macOS + +Raise your app's deployment targets. A lower target fails the build. + +```ruby +# ios/Podfile +platform :ios, '15.0' + +# macos/Podfile +platform :osx, '11.0' +``` + +Also set `IPHONEOS_DEPLOYMENT_TARGET` and `MACOSX_DEPLOYMENT_TARGET` in your Xcode projects, including when you build with Swift Package Manager. + +### Android + +`posthog-android` has required `minSdkVersion` 23 since 3.39.0, so an app on 21 or 22 already could not build with this plugin. The plugin now declares the same value, and you have nothing to change. + +### Native SDKs + + + +The native dependency ranges are unchanged by this release: `posthog-ios` `>= 3.92.3, < 4.0.0` and `posthog-android` `[3.72.9, 4.0.0)`. + +## Breaking API changes + +These all fail at compile time unless noted otherwise. + +### `getFeatureFlagResult(sendEvent:)` is now `sendFeatureFlagEvent:` + +The parameter is also now `bool?`. See [the default change](#getfeatureflagresult-follows-your-config) below. + +```dart +// Before +await Posthog().getFeatureFlagResult('my-flag', sendEvent: false); + +// After +await Posthog().getFeatureFlagResult('my-flag', sendFeatureFlagEvent: false); +``` + +### `PostHogConfig.apiKey` is removed + +Read `projectToken` instead. The constructor already takes the token as its first argument. + +```dart +// Before +final token = config.apiKey; + +// After +final token = config.projectToken; +``` + +### `com.posthog.posthog.API_KEY` is removed + +Apps that start the SDK from `AndroidManifest.xml` or `Info.plist` must rename the key. **This does not fail at build time.** With only the old key, the SDK logs an error and does not start, so the app sends no data. + +```xml + + + + + +``` + +```xml + +com.posthog.posthog.API_KEY + + + +com.posthog.posthog.PROJECT_TOKEN + +``` + +### `Posthog().enable()` and `disable()` are removed + +```dart +// Before +await Posthog().disable(); +await Posthog().enable(); + +// After +await Posthog().optOut(); +await Posthog().optIn(); +``` + +### `PostHogConfig.sendFeatureFlagEvents` is removed + +```dart +// Before +config.sendFeatureFlagEvents = false; + +// After +config.sendFeatureFlagEvent = false; +``` + +### `Posthog().getFeatureFlagPayload()` is removed + +`getFeatureFlagPayload()` never captured `$feature_flag_called` on iOS, macOS, Android, Windows or Linux. Pass `sendFeatureFlagEvent: false` to keep it that way. Without it, the replacement call captures the event when your config allows it. + +```dart +// Before +final payload = await Posthog().getFeatureFlagPayload('my-flag'); + +// After +final result = await Posthog().getFeatureFlagResult( + 'my-flag', + sendFeatureFlagEvent: false, +); +final payload = result?.payload; +``` + +### `PostHogSessionReplayConfig.debouncerDelay` is removed + +```dart +// Before +config.sessionReplayConfig.debouncerDelay = const Duration(seconds: 2); + +// After +config.sessionReplayConfig.throttleDelay = const Duration(seconds: 2); +``` + +### `PostHogDataMode.cellular` is removed + +It always behaved like `any`, so sending does not change. + +```dart +// Before +config.dataMode = PostHogDataMode.cellular; + +// After +config.dataMode = PostHogDataMode.any; +``` + +### `toMap()` and `PostHogFeatureFlagResult.fromMap()` are removed + +They serialized the internal platform-channel format and have no replacement. `toMap()` is gone from `PostHogConfig`, `PostHogBootstrapConfig`, `PostHogLogsConfig`, `PostHogRageClickConfig`, `PostHogSessionReplayConfig`, `PostHogErrorTrackingConfig` and `PostHogExceptionStepsConfig`. To build a result, for example in a test, call the `PostHogFeatureFlagResult` constructor, which stays public. + +### Platform interface changes + +These affect only code that calls, extends or fakes `PosthogFlutterPlatformInterface`, `PosthogFlutterIO` or `PosthogFlutterWeb` directly, such as test mocks. `Posthog().optIn()` and `optOut()` are unaffected. + +- `enable()` and `disable()` are now `optIn()` and `optOut()`. +- `getFeatureFlagPayload()` is removed. +- `getFeatureFlagResult({required String key, bool sendEvent = true})` is now `getFeatureFlagResult({required String key, bool? sendFeatureFlagEvent})`. + +```dart +// Before +@override +Future disable() async {} + +// After +@override +Future optOut() async {} +``` + +## Behavior changes + +### `getFeatureFlagResult()` follows your config + +**This is a silent change with no compile error.** When you omit `sendFeatureFlagEvent`, `getFeatureFlagResult()` now uses `PostHogConfig.sendFeatureFlagEvent` instead of always capturing `$feature_flag_called`. This matches `getFeatureFlag()` and `isFeatureEnabled()`. + +It only affects apps that set `config.sendFeatureFlagEvent = false`. Those apps stop sending `$feature_flag_called` from `getFeatureFlagResult()`, so insights and experiments that rely on that event can receive less data. To keep the event for a call: + +```dart +await Posthog().getFeatureFlagResult('my-flag', sendFeatureFlagEvent: true); +``` + +On web, `PostHogConfig.sendFeatureFlagEvent` is not applied to posthog-js, so an omitted value keeps the posthog-js default, which captures the event. Pass `sendFeatureFlagEvent: false` to suppress it on web. + +## Upgrade checklist + +- [ ] Raise iOS to 15.0 and macOS to 11.0 in your Podfiles and Xcode projects. +- [ ] Rename `com.posthog.posthog.API_KEY` to `com.posthog.posthog.PROJECT_TOKEN` in `AndroidManifest.xml` and `Info.plist`. +- [ ] If you set `sendFeatureFlagEvent = false`, check whether anything relies on `$feature_flag_called` from `getFeatureFlagResult()`. +- [ ] Add `sendFeatureFlagEvent: false` wherever you replace `getFeatureFlagPayload()`. +- [ ] Fix the remaining compile errors using the sections above, including any test fakes of the platform classes. From 38ab66cf4aceeb8067364133564ca01e26bd914b Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Fri, 30 Oct 2026 22:09:15 -0400 Subject: [PATCH 12/12] docs: document the move to Swift Package Manager in 6.0 --- .changeset/v6-major-release.md | 1 + posthog_flutter/MIGRATION.md | 34 +++++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/.changeset/v6-major-release.md b/.changeset/v6-major-release.md index 51a38c5c..9d30fb0e 100644 --- a/.changeset/v6-major-release.md +++ b/.changeset/v6-major-release.md @@ -3,6 +3,7 @@ --- - **Breaking:** bump the minimum iOS deployment target from 13.0 to 15.0 and macOS from 10.15 to 11.0 — see the [migration guide](https://github.com/PostHog/posthog-flutter/blob/main/posthog_flutter/MIGRATION.md) +- **Breaking:** remove CocoaPods support on iOS and macOS — install through Swift Package Manager, which needs Flutter 3.41 or later and doesn't support add-to-app modules yet - **Breaking:** change `getFeatureFlagResult()` to follow `PostHogConfig.sendFeatureFlagEvent` on iOS, macOS, Android, Windows and Linux when `sendFeatureFlagEvent` is omitted — pass `sendFeatureFlagEvent: true` to keep capturing `$feature_flag_called` - **Breaking:** rename `getFeatureFlagResult(sendEvent:)` to `getFeatureFlagResult(sendFeatureFlagEvent:)` - **Breaking:** remove `PostHogConfig.apiKey` and the `com.posthog.posthog.API_KEY` manifest and Info.plist key — use `projectToken` and `com.posthog.posthog.PROJECT_TOKEN` diff --git a/posthog_flutter/MIGRATION.md b/posthog_flutter/MIGRATION.md index cd8cb887..0ba88922 100644 --- a/posthog_flutter/MIGRATION.md +++ b/posthog_flutter/MIGRATION.md @@ -7,12 +7,16 @@ | iOS deployment target | 13.0 | 15.0 | | macOS deployment target | 10.15 | 11.0 | | Android `minSdkVersion` declared by the plugin | 21 | 23 | +| iOS and macOS dependency manager | CocoaPods or Swift Package Manager | Swift Package Manager only | +| Flutter for iOS and macOS builds | 3.27 | 3.41 | -The Dart (`>=3.6.0`) and Flutter (`>=3.27.0`) minimums are unchanged. +The Dart minimum (`>=3.6.0`) is unchanged. Android, web, Windows and Linux builds aren't affected by the Swift Package Manager requirement. ### iOS and macOS -Raise your app's deployment targets. A lower target fails the build. +Raise your app's deployment targets. A lower target fails the build. Set **Minimum Deployments** for the `Runner` target in Xcode (**Runner > General > Minimum Deployments**), which sets `IPHONEOS_DEPLOYMENT_TARGET` and `MACOSX_DEPLOYMENT_TARGET`. + +If you keep a `Podfile` for other plugins, raise its platform too: ```ruby # ios/Podfile @@ -22,7 +26,26 @@ platform :ios, '15.0' platform :osx, '11.0' ``` -Also set `IPHONEOS_DEPLOYMENT_TARGET` and `MACOSX_DEPLOYMENT_TARGET` in your Xcode projects, including when you build with Swift Package Manager. +### Move from CocoaPods to Swift Package Manager + +posthog_flutter 6.0 has no podspec. On iOS and macOS, it installs only through Swift Package Manager. + +**If you ignore this, the build stops.** With Swift Package Manager turned off, `flutter build` exits with `Plugin posthog_flutter is only compatible with Swift Package Manager`. + +1. Use Flutter 3.41 or later. The plugin's `Package.swift` depends on the `FlutterFramework` package, which Flutter generates starting with 3.41. +2. Turn Swift Package Manager on if it's off. It's on by default from Flutter 3.44. On 3.41 to 3.43, or if you turned it off, run: + + ```sh + flutter config --enable-swift-package-manager + ``` + +3. Run your app. Flutter adds Swift Package Manager to the Xcode project on its own. + +Other plugins that only support CocoaPods keep working: Flutter installs them through your `Podfile` and installs posthog_flutter as a Swift package. Keep the `Podfile` in that case. + +If every plugin in your app supports Swift Package Manager, you can remove CocoaPods. This is optional. Follow [Flutter's steps](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers#how-to-remove-cocoapods-integration). + +**Add-to-app:** Flutter doesn't support Swift Package Manager in Flutter modules yet ([flutter/flutter#146957](https://github.com/flutter/flutter/issues/146957)), and its build stops for a plugin without a podspec. If you embed Flutter as a module in an iOS or macOS app, stay on posthog_flutter 5.x until Flutter adds support. ### Android @@ -34,7 +57,6 @@ Also set `IPHONEOS_DEPLOYMENT_TARGET` and `MACOSX_DEPLOYMENT_TARGET` in your Xco PENDING, not on this branch yet. PR #673 (which contains #649) moves the plugin to posthog-ios 4.x and posthog-android 4.x. Neither is released. When it merges, replace this comment with a section covering: - Xcode 26 required. -- posthog-ios 4.0 is not published to CocoaPods, so `s.dependency 'PostHog', '>= 4.0.0'` cannot resolve: state what CocoaPods apps must do. - onFeatureFlags receives, and reloadFeatureFlags() returns, PostHogFeatureFlagsLoaded. - Capture v1 endpoints and what reverse proxies must forward. - iOS and Android: malformed, empty or whitespace-only flag payloads become null. iOS: bootstrapped string payloads stay strings. @@ -193,7 +215,9 @@ On web, `PostHogConfig.sendFeatureFlagEvent` is not applied to posthog-js, so an ## Upgrade checklist -- [ ] Raise iOS to 15.0 and macOS to 11.0 in your Podfiles and Xcode projects. +- [ ] Use Flutter 3.41 or later and make sure Swift Package Manager is on. +- [ ] Don't upgrade yet if you embed Flutter as an iOS or macOS add-to-app module. +- [ ] Raise iOS to 15.0 and macOS to 11.0 in your Xcode projects, and in any `Podfile` you keep. - [ ] Rename `com.posthog.posthog.API_KEY` to `com.posthog.posthog.PROJECT_TOKEN` in `AndroidManifest.xml` and `Info.plist`. - [ ] If you set `sendFeatureFlagEvent = false`, check whether anything relies on `$feature_flag_called` from `getFeatureFlagResult()`. - [ ] Add `sendFeatureFlagEvent: false` wherever you replace `getFeatureFlagPayload()`.