From 5d8fa73e49b4e53382da3121e27d821678ea5f54 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Tue, 14 Jul 2026 17:11:16 +0300 Subject: [PATCH 01/13] feat: add bootstrap option to PostHogConfig --- .changeset/proud-owls-bake.md | 5 + api/posthog_flutter.api.json | 135 ++++++++++++++++++ .../posthog/flutter/PosthogFlutterPlugin.kt | 16 ++- .../PosthogFlutterPlugin.swift | 12 ++ posthog_flutter/lib/src/posthog_config.dart | 86 +++++++++++ posthog_flutter/test/posthog_test.dart | 54 +++++++ 6 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 .changeset/proud-owls-bake.md diff --git a/.changeset/proud-owls-bake.md b/.changeset/proud-owls-bake.md new file mode 100644 index 00000000..0c2ecf87 --- /dev/null +++ b/.changeset/proud-owls-bake.md @@ -0,0 +1,5 @@ +--- +"posthog_flutter": minor +--- + +Add a `bootstrap` option to `PostHogConfig` for pre-seeding identity and feature flags before the first `/flags` response. Set `config.bootstrap = PostHogBootstrapConfig(...)` before `setup()` so early events carry a caller-controlled distinct ID and flag reads return your values during cold start. The values are forwarded to the native iOS and Android SDKs and mirror the `bootstrap` option in posthog-js. On Flutter web, configure `bootstrap` in your `posthog.init` call instead. diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index bfbda2e1..3584ba52 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -2125,6 +2125,17 @@ "relativePath": "lib/src/posthog_config.dart", "typeName": "PostHogLogsConfig" }, + { + "entryPoints": [], + "isDeprecated": false, + "isExperimental": false, + "isReadable": true, + "isStatic": false, + "isWriteable": true, + "name": "bootstrap", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "PostHogBootstrapConfig?" + }, { "entryPoints": [], "isDeprecated": false, @@ -2765,6 +2776,130 @@ ], "typeParameterNames": [] }, + { + "entryPoints": [ + "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, + "isExperimental": false, + "isStatic": false, + "name": "new", + "parameters": [ + { + "isDeprecated": false, + "isExperimental": false, + "isNamed": true, + "isRequired": false, + "name": "distinctId", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "String?" + }, + { + "isDeprecated": false, + "isExperimental": false, + "isNamed": true, + "isRequired": false, + "name": "isIdentifiedId", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "bool" + }, + { + "isDeprecated": false, + "isExperimental": false, + "isNamed": true, + "isRequired": false, + "name": "featureFlags", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "Map?" + }, + { + "isDeprecated": false, + "isExperimental": false, + "isNamed": true, + "isRequired": false, + "name": "featureFlagPayloads", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "Map?" + } + ], + "relativePath": "lib/src/posthog_config.dart", + "returnTypeName": "PostHogBootstrapConfig", + "type": "constructor", + "typeParameterNames": [] + } + ], + "fieldDeclarations": [ + { + "entryPoints": [], + "isDeprecated": false, + "isExperimental": false, + "isReadable": true, + "isStatic": false, + "isWriteable": false, + "name": "distinctId", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "String?" + }, + { + "entryPoints": [], + "isDeprecated": false, + "isExperimental": false, + "isReadable": true, + "isStatic": false, + "isWriteable": false, + "name": "isIdentifiedId", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "bool" + }, + { + "entryPoints": [], + "isDeprecated": false, + "isExperimental": false, + "isReadable": true, + "isStatic": false, + "isWriteable": false, + "name": "featureFlags", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "Map?" + }, + { + "entryPoints": [], + "isDeprecated": false, + "isExperimental": false, + "isReadable": true, + "isStatic": false, + "isWriteable": false, + "name": "featureFlagPayloads", + "relativePath": "lib/src/posthog_config.dart", + "typeName": "Map?" + } + ], + "isDeprecated": false, + "isExperimental": false, + "isRequired": false, + "isSealed": false, + "name": "PostHogBootstrapConfig", + "relativePath": "lib/src/posthog_config.dart", + "superTypeNames": [ + "Object" + ], + "typeParameterNames": [] + }, { "entryPoints": [ "posthog_flutter.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 f3de27d7..63476329 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 @@ -21,6 +21,7 @@ import android.view.ViewGroup import androidx.annotation.RequiresApi import com.posthog.PersonProfiles import com.posthog.PostHog +import com.posthog.PostHogBootstrapConfig import com.posthog.PostHogConfig import com.posthog.PostHogOnFeatureFlags import com.posthog.android.PostHogAndroid @@ -488,6 +489,18 @@ class PosthogFlutterPlugin : } } + // Bootstrap precedence and flag layering live in the native SDK; forward values only. + posthogConfig.getIfNotNull>("bootstrap") { bootstrap -> + @Suppress("UNCHECKED_CAST") + this.bootstrap = + PostHogBootstrapConfig( + distinctId = bootstrap["distinctId"] as? String, + isIdentifiedId = bootstrap["isIdentifiedId"] as? Boolean ?: false, + featureFlags = bootstrap["featureFlags"] as? Map, + featureFlagPayloads = bootstrap["featureFlagPayloads"] as? Map, + ) + } + sdkName = "posthog-flutter" sdkVersion = postHogVersion @@ -810,7 +823,8 @@ class PosthogFlutterPlugin : // different platform view (e.g. a masked map) that merely overlaps, so // compositing it would leak masked content. Slack absorbs rounding. val tolerance = 8 - if (destX < -tolerance || destY < -tolerance || + if (destX < -tolerance || + destY < -tolerance || destX + svLogW > destBitmap.width + tolerance || destY + svLogH > destBitmap.height + tolerance ) { 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 8972bfc4..d5e56665 100644 --- a/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift +++ b/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift @@ -253,6 +253,18 @@ public class PosthogFlutterPlugin: NSObject, FlutterPlugin { } } + // Bootstrap precedence and flag layering live in the native SDK; forward values only. + if let bootstrap = posthogConfig["bootstrap"] as? [String: Any] { + let bootstrapConfig = PostHogBootstrapConfig() + bootstrapConfig.distinctId = bootstrap["distinctId"] as? String + if let isIdentifiedId = bootstrap["isIdentifiedId"] as? Bool { + bootstrapConfig.isIdentifiedId = isIdentifiedId + } + bootstrapConfig.featureFlags = bootstrap["featureFlags"] as? [String: Any] + bootstrapConfig.featureFlagPayloads = bootstrap["featureFlagPayloads"] as? [String: Any] + config.bootstrap = bootstrapConfig + } + // Update SDK name and version postHogSdkName = "posthog-flutter" postHogVersion = postHogFlutterVersion diff --git a/posthog_flutter/lib/src/posthog_config.dart b/posthog_flutter/lib/src/posthog_config.dart index ffa3f482..9ada9818 100644 --- a/posthog_flutter/lib/src/posthog_config.dart +++ b/posthog_flutter/lib/src/posthog_config.dart @@ -175,6 +175,23 @@ class PostHogConfig { /// `Posthog().logger` facade). final logsConfig = PostHogLogsConfig(); + /// Pre-seeded identity and feature-flag state applied on the very first SDK + /// launch, before any network request completes. + /// + /// Set this before calling `Posthog().setup(config)` so events captured during + /// cold start carry a caller-controlled `$distinct_id` and feature-flag reads + /// return caller-provided values before the first `/flags` response. Mirrors + /// the [`bootstrap` option in `posthog-js`](https://posthog.com/docs/feature-flags/bootstrapping). + /// + /// Forwarded to the native iOS/Android SDKs, which apply all precedence rules + /// (never overwrite persisted identity, overlay loaded flags over bootstrapped + /// ones, drop the bootstrap on `reset()`). Defaults to `null` (no bootstrap). + /// + /// **Flutter web:** not applied. The web SDK hooks onto an already-initialized + /// posthog-js instance, so configure `bootstrap` in your `posthog.init({...})` + /// call instead. + PostHogBootstrapConfig? bootstrap; + /// Callback to be invoked when feature flags are loaded. /// /// Use [Posthog.getFeatureFlag] or [Posthog.isFeatureEnabled] within this @@ -303,6 +320,75 @@ class PostHogConfig { 'sessionReplayConfig': sessionReplayConfig.toMap(), 'errorTrackingConfig': errorTrackingConfig.toMap(), 'logs': logsConfig.toMap(), + if (bootstrap != null) 'bootstrap': bootstrap!.toMap(), + }; + } +} + +/// Pre-seeded identity and feature-flag state applied on the very first SDK +/// launch, before any network request completes. +/// +/// Assign an instance to [PostHogConfig.bootstrap] before calling +/// `Posthog().setup(config)`. The values are forwarded to the native iOS/Android +/// SDKs, which own all bootstrap behavior: +/// +/// - Bootstrapped identity seeds the very first session only. It is applied only +/// when no identity is persisted for that scope, and never overwrites an +/// existing user. An anonymous bootstrap ([isIdentifiedId] `false`) seeds the +/// anonymous id; an identified bootstrap ([isIdentifiedId] `true`) seeds the +/// distinct id and marks the user identified (merging an existing anonymous +/// user via `identify()`, or preserving a different identified user with a +/// warning) — it never becomes the device id. +/// - Bootstrapped feature flags form a base layer only: values loaded from +/// `/flags` overlay them for overlapping keys, while bootstrapped-only keys +/// remain available. The base layer is dropped on `reset()`. +/// +/// **Flutter web:** not applied. Configure `bootstrap` in your +/// `posthog.init({...})` call instead. +class PostHogBootstrapConfig { + /// Creates a bootstrap configuration. + /// + /// Pass only the dimensions you want to seed; leave the rest `null`. + const PostHogBootstrapConfig({ + this.distinctId, + this.isIdentifiedId = false, + this.featureFlags, + this.featureFlagPayloads, + }); + + /// The distinct id to seed on first launch. + /// + /// When [isIdentifiedId] is `false` (the default) this becomes the anonymous + /// id — the `$distinct_id` on pre-identify events. When `true` it is treated + /// as an already-identified user's distinct id. + final String? distinctId; + + /// Whether [distinctId] represents an already-identified user. + /// + /// Defaults to `false`. Set to `true` when the host application resolved the + /// user's identity outside the SDK (for example from a backend session token). + final bool isIdentifiedId; + + /// Feature flag values served until the first `/flags` response arrives, + /// keyed by flag key. Each value is a `bool` for boolean flags or a `String` + /// for multivariate flags. + final Map? featureFlags; + + /// JSON payloads paired with [featureFlags], keyed by flag key. Each value is + /// the already-decoded payload (map, list, string, number, ...). + 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() { + return { + if (distinctId != null) 'distinctId': distinctId, + 'isIdentifiedId': isIdentifiedId, + if (featureFlags != null) 'featureFlags': featureFlags, + if (featureFlagPayloads != null) + 'featureFlagPayloads': featureFlagPayloads, }; } } diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index 97475e3e..31b03049 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -155,6 +155,60 @@ void main() { config.toMap()['sessionReplayConfig'] as Map; expect(updatedReplayConfig['maskAllPlatformViews'], isFalse); }); + + test('omits bootstrap from toMap when not set', () { + final config = PostHogConfig('test_project_token'); + + expect(config.bootstrap, isNull); + expect(config.toMap().containsKey('bootstrap'), isFalse); + }); + + test('serializes an identified identity bootstrap', () { + final config = PostHogConfig('test_project_token') + ..bootstrap = const PostHogBootstrapConfig( + distinctId: 'user-123', + isIdentifiedId: true, + ); + + final bootstrap = config.toMap()['bootstrap'] as Map; + expect(bootstrap['distinctId'], equals('user-123')); + expect(bootstrap['isIdentifiedId'], isTrue); + expect(bootstrap.containsKey('featureFlags'), isFalse); + expect(bootstrap.containsKey('featureFlagPayloads'), isFalse); + }); + + test('defaults isIdentifiedId to false and serializes it', () { + final config = PostHogConfig('test_project_token') + ..bootstrap = const PostHogBootstrapConfig(distinctId: 'anon-abc'); + + final bootstrap = config.toMap()['bootstrap'] as Map; + expect(bootstrap['distinctId'], equals('anon-abc')); + expect(bootstrap['isIdentifiedId'], isFalse); + }); + + test('serializes feature flags and payloads without identity', () { + final config = PostHogConfig('test_project_token') + ..bootstrap = const PostHogBootstrapConfig( + featureFlags: {'beta-ui': 'variant-a', 'legacy': true}, + featureFlagPayloads: { + 'beta-ui': {'color': 'blue'}, + }, + ); + + final bootstrap = config.toMap()['bootstrap'] as Map; + expect(bootstrap.containsKey('distinctId'), isFalse); + expect(bootstrap['isIdentifiedId'], isFalse); + expect( + bootstrap['featureFlags'], + equals({'beta-ui': 'variant-a', 'legacy': true}), + ); + expect( + bootstrap['featureFlagPayloads'], + equals({ + 'beta-ui': {'color': 'blue'}, + }), + ); + }); }); group('PostHogPlatformView', () { From 64835a7803f98f779d77ffe113bfb315597774e5 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Tue, 14 Jul 2026 17:11:49 +0300 Subject: [PATCH 02/13] chore: require native SDKs with bootstrap and wire local dev overrides --- CONTRIBUTING.md | 54 +++++++++---------- example/android/.gitignore | 3 ++ example/android/settings.gradle.kts | 5 ++ example/ios/.gitignore | 3 ++ example/ios/Podfile | 6 +++ posthog_flutter/android/build.gradle | 2 +- .../darwin/posthog_flutter.podspec | 2 +- .../darwin/posthog_flutter/Package.swift | 2 +- 8 files changed, 44 insertions(+), 33 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 82930c0f..288b3146 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,45 +41,39 @@ flutter build macos ## Testing with local native SDKs +The native SDK version floors in `posthog_flutter/darwin/posthog_flutter.podspec`, +`posthog_flutter/darwin/posthog_flutter/Package.swift`, and +`posthog_flutter/android/build.gradle` track the next expected native release. To +develop against an unreleased native branch, point the example app at your local +checkout with a gitignored override file — the committed build stays release-clean, +and a source override ignores the version floor. + ### iOS -- Update `example/ios/Podfile` to override the `PostHog` pod with your local path: +- Create `example/ios/Podfile.local` (gitignored) pointing at your checkout: ```ruby -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - - # add this - pod 'PostHog', :path => File.expand_path('~/posthog-ios') -end +pod 'PostHog', :path => File.expand_path('~/posthog-ios') ``` -- Run `cd example/ios && pod install` to install the local pod -- Open iOS simulator -- Run the app with `flutter run` - -### Android - -In your local `posthog-android` repo: +- Run `cd example/ios && pod install`, then `flutter run`. -- Run `make dryRelease` to build and publish the package to Maven local +The example `Podfile` evaluates `Podfile.local` when present, so no committed file +changes. -In the `posthog-flutter` repo: +### Android -- Update `/android/build.gradle` to add `mavenLocal()` as a repository: +- Create `example/android/settings.local.gradle.kts` (gitignored) with a composite + build of your checkout: -```gradle -allprojects { - repositories { - mavenLocal() // add this - google() - mavenCentral() - } -} +```kotlin +// absolute path — Kotlin does not expand "~" +includeBuild("/absolute/path/to/posthog-android") ``` -- Open Android simulator -- Run the app with `flutter run` +- Run `flutter run`. + +Gradle substitutes `com.posthog:posthog(-android)` with the local source regardless +of the version floor, so no publishing or `mavenLocal()` is needed. As a fallback you +can still `make dryRelease` in `posthog-android` (add `-PandroidVersion=` so the +published version satisfies the floor) and add `mavenLocal()` to the repositories. diff --git a/example/android/.gitignore b/example/android/.gitignore index 6f568019..7d35ea3d 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -6,6 +6,9 @@ gradle-wrapper.jar /local.properties GeneratedPluginRegistrant.java +# Local development override for an unreleased posthog-android (see CONTRIBUTING.md). +settings.local.gradle.kts + # Remember to never publicly share your keystore. # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app key.properties diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts index f6198875..0f180cf7 100644 --- a/example/android/settings.gradle.kts +++ b/example/android/settings.gradle.kts @@ -24,3 +24,8 @@ plugins { } include(":app") + +// Local development against an unreleased posthog-android. Inert unless a +// gitignored settings.local.gradle.kts exists; see CONTRIBUTING.md. +val localSettings = file("settings.local.gradle.kts") +if (localSettings.exists()) apply(from = localSettings) diff --git a/example/ios/.gitignore b/example/ios/.gitignore index 7a7f9873..d2a8090f 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -27,6 +27,9 @@ Flutter/flutter_export_environment.sh ServiceDefinitions.json Runner/GeneratedPluginRegistrant.* +# Local development override for an unreleased PostHog iOS SDK (see CONTRIBUTING.md). +Podfile.local + # Exceptions to above rules. !default.mode1v3 !default.mode2v3 diff --git a/example/ios/Podfile b/example/ios/Podfile index 04c36cf4..266a1cd0 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -32,6 +32,12 @@ target 'Runner' do use_modular_headers! flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + + # Local development against an unreleased PostHog iOS SDK. Inert unless a + # gitignored Podfile.local exists; see CONTRIBUTING.md. + podfile_local = File.expand_path('Podfile.local', __dir__) + instance_eval(File.read(podfile_local), podfile_local) if File.exist?(podfile_local) + target 'RunnerTests' do inherit! :search_paths end diff --git a/posthog_flutter/android/build.gradle b/posthog_flutter/android/build.gradle index e45aa21c..381b989a 100644 --- a/posthog_flutter/android/build.gradle +++ b/posthog_flutter/android/build.gradle @@ -65,7 +65,7 @@ android { testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'org.mockito:mockito-core:5.0.0' // + Version 3.51.0 and the versions up to 4.0.0, not including 4.0.0 and higher - implementation 'com.posthog:posthog-android:[3.51.0,4.0.0]' + implementation 'com.posthog:posthog-android:[3.53.0,4.0.0]' } testOptions { diff --git a/posthog_flutter/darwin/posthog_flutter.podspec b/posthog_flutter/darwin/posthog_flutter.podspec index be2cc262..0b247bc9 100644 --- a/posthog_flutter/darwin/posthog_flutter.podspec +++ b/posthog_flutter/darwin/posthog_flutter.podspec @@ -22,7 +22,7 @@ Postog flutter plugin s.osx.dependency 'FlutterMacOS' # ~> Version 3.61.0 up to, but not including, 4.0.0 - s.dependency 'PostHog', '>= 3.61.0', '< 4.0.0' + s.dependency 'PostHog', '>= 3.65.0', '< 4.0.0' s.ios.deployment_target = '13.0' # PH iOS SDK 3.0.0 requires >= 10.15 diff --git a/posthog_flutter/darwin/posthog_flutter/Package.swift b/posthog_flutter/darwin/posthog_flutter/Package.swift index c10b10dc..87046c09 100644 --- a/posthog_flutter/darwin/posthog_flutter/Package.swift +++ b/posthog_flutter/darwin/posthog_flutter/Package.swift @@ -14,7 +14,7 @@ let package = Package( ], dependencies: [ .package(name: "FlutterFramework", path: "../FlutterFramework"), - .package(url: "https://github.com/PostHog/posthog-ios", "3.61.0" ..< "4.0.0"), + .package(url: "https://github.com/PostHog/posthog-ios", "3.65.0" ..< "4.0.0"), ], targets: [ .target( From 6a64c6e402e72ae608e2cdd0c1edc9053299f7f2 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Tue, 14 Jul 2026 17:11:49 +0300 Subject: [PATCH 03/13] test: forward bootstrap config in the compliance adapter --- sdk_compliance_adapter/lib/adapter_server.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sdk_compliance_adapter/lib/adapter_server.dart b/sdk_compliance_adapter/lib/adapter_server.dart index 5f6a3a40..97519fac 100644 --- a/sdk_compliance_adapter/lib/adapter_server.dart +++ b/sdk_compliance_adapter/lib/adapter_server.dart @@ -97,6 +97,18 @@ class ComplianceAdapter { ..preloadFeatureFlags = false ..captureApplicationLifecycleEvents = false ..debug = true; + + final bootstrap = _readNullableObjectMap(body['bootstrap']); + if (bootstrap != null) { + config.bootstrap = PostHogBootstrapConfig( + distinctId: bootstrap['distinct_id'] as String?, + isIdentifiedId: bootstrap['is_identified_id'] == true, + featureFlags: _readObjectMap(bootstrap['feature_flags']), + featureFlagPayloads: + _readObjectMap(bootstrap['feature_flag_payloads']), + ); + } + await Posthog().setup(config); await _sendJson(request, {'success': true}); From bbe3e208287bda1a6da450767f000d76a3417b24 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Wed, 15 Jul 2026 12:02:47 +0300 Subject: [PATCH 04/13] chore: track native bootstrap release versions (ios 3.66.0, android 3.54.0) --- posthog_flutter/android/build.gradle | 2 +- posthog_flutter/darwin/posthog_flutter.podspec | 2 +- posthog_flutter/darwin/posthog_flutter/Package.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/posthog_flutter/android/build.gradle b/posthog_flutter/android/build.gradle index 381b989a..26c2143a 100644 --- a/posthog_flutter/android/build.gradle +++ b/posthog_flutter/android/build.gradle @@ -65,7 +65,7 @@ android { testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'org.mockito:mockito-core:5.0.0' // + Version 3.51.0 and the versions up to 4.0.0, not including 4.0.0 and higher - implementation 'com.posthog:posthog-android:[3.53.0,4.0.0]' + implementation 'com.posthog:posthog-android:[3.54.0,4.0.0]' } testOptions { diff --git a/posthog_flutter/darwin/posthog_flutter.podspec b/posthog_flutter/darwin/posthog_flutter.podspec index 0b247bc9..d85c0bc3 100644 --- a/posthog_flutter/darwin/posthog_flutter.podspec +++ b/posthog_flutter/darwin/posthog_flutter.podspec @@ -22,7 +22,7 @@ Postog flutter plugin s.osx.dependency 'FlutterMacOS' # ~> Version 3.61.0 up to, but not including, 4.0.0 - s.dependency 'PostHog', '>= 3.65.0', '< 4.0.0' + s.dependency 'PostHog', '>= 3.66.0', '< 4.0.0' s.ios.deployment_target = '13.0' # PH iOS SDK 3.0.0 requires >= 10.15 diff --git a/posthog_flutter/darwin/posthog_flutter/Package.swift b/posthog_flutter/darwin/posthog_flutter/Package.swift index 87046c09..3998a97d 100644 --- a/posthog_flutter/darwin/posthog_flutter/Package.swift +++ b/posthog_flutter/darwin/posthog_flutter/Package.swift @@ -14,7 +14,7 @@ let package = Package( ], dependencies: [ .package(name: "FlutterFramework", path: "../FlutterFramework"), - .package(url: "https://github.com/PostHog/posthog-ios", "3.65.0" ..< "4.0.0"), + .package(url: "https://github.com/PostHog/posthog-ios", "3.66.0" ..< "4.0.0"), ], targets: [ .target( From 768b33f799b284d31a01e9bff3e9a7e2e6a66975 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Wed, 15 Jul 2026 12:02:47 +0300 Subject: [PATCH 05/13] docs: align bootstrap flag docs with the enabled-only serving model --- posthog_flutter/lib/src/posthog_config.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/posthog_flutter/lib/src/posthog_config.dart b/posthog_flutter/lib/src/posthog_config.dart index 9ada9818..d8b38d4a 100644 --- a/posthog_flutter/lib/src/posthog_config.dart +++ b/posthog_flutter/lib/src/posthog_config.dart @@ -339,9 +339,10 @@ class PostHogConfig { /// distinct id and marks the user identified (merging an existing anonymous /// user via `identify()`, or preserving a different identified user with a /// warning) — it never becomes the device id. -/// - Bootstrapped feature flags form a base layer only: values loaded from -/// `/flags` overlay them for overlapping keys, while bootstrapped-only keys -/// remain available. The base layer is dropped on `reset()`. +/// - Only enabled bootstrapped flags are served (a `true` or a non-empty +/// variant string; `false` or empty values are dropped). They are served +/// until the first `/flags` response, which then takes over, and they are +/// dropped on `reset()`. /// /// **Flutter web:** not applied. Configure `bootstrap` in your /// `posthog.init({...})` call instead. @@ -371,7 +372,8 @@ class PostHogBootstrapConfig { /// Feature flag values served until the first `/flags` response arrives, /// keyed by flag key. Each value is a `bool` for boolean flags or a `String` - /// for multivariate flags. + /// for multivariate flags. Only enabled values are served: `false` or an + /// empty string is dropped. final Map? featureFlags; /// JSON payloads paired with [featureFlags], keyed by flag key. Each value is From 318a2de07eb305cde27f437afbc8e10d77f8a98b Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Wed, 15 Jul 2026 15:56:52 +0300 Subject: [PATCH 06/13] fix(bootstrap): warn when a bootstrap featureFlags value is not bool/String --- posthog_flutter/lib/src/posthog_config.dart | 15 +++++++++++ posthog_flutter/test/posthog_test.dart | 29 +++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/posthog_flutter/lib/src/posthog_config.dart b/posthog_flutter/lib/src/posthog_config.dart index d8b38d4a..d55f34d9 100644 --- a/posthog_flutter/lib/src/posthog_config.dart +++ b/posthog_flutter/lib/src/posthog_config.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'logs/posthog_log_record.dart'; import 'posthog_event.dart'; import 'posthog_flutter_platform_interface.dart'; +import 'util/logging.dart'; /// Callback to intercept and modify events before they are sent to PostHog. /// @@ -385,6 +386,20 @@ class 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, diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index 31b03049..ae3fae74 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:posthog_flutter/posthog_flutter.dart'; @@ -209,6 +211,33 @@ void main() { }), ); }); + + test('warns on a non-bool/String featureFlags value but still forwards it', + () { + final config = PostHogConfig('test_project_token') + ..bootstrap = const PostHogBootstrapConfig( + featureFlags: {'discount-tier': 2, 'beta-ui': 'variant-a'}, + ); + + final logs = []; + final map = runZoned( + config.toMap, + zoneSpecification: ZoneSpecification( + print: (_, __, ___, line) => logs.add(line), + ), + ); + + // The mismatched value is still forwarded; the native SDK drops it. + final bootstrap = map['bootstrap'] as Map; + expect( + bootstrap['featureFlags'], + equals({'discount-tier': 2, 'beta-ui': 'variant-a'}), + ); + // ...but the caller gets a breadcrumb for the ignored entry, and no + // false warning for the valid String value. + expect(logs.where((l) => l.contains('discount-tier')), isNotEmpty); + expect(logs.where((l) => l.contains('beta-ui')), isEmpty); + }); }); group('PostHogPlatformView', () { From 59ad467bbe229c61deeaa4a62fff183a418c7983 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Thu, 16 Jul 2026 12:03:56 +0300 Subject: [PATCH 07/13] chore: require posthog-android 3.55.0 and repin ios example to 3.66.1 --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- posthog_flutter/android/build.gradle | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1ed32bf8..4c014b3b 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/PostHog/posthog-ios", "state" : { - "revision" : "9fcdb83e79b155c0dae104eadf5c269079dcbddb", - "version" : "3.62.0" + "revision" : "19288ee2f0575606b4669b2abadda2c9db97c529", + "version" : "3.66.1" } } ], diff --git a/posthog_flutter/android/build.gradle b/posthog_flutter/android/build.gradle index 26c2143a..7bb04d06 100644 --- a/posthog_flutter/android/build.gradle +++ b/posthog_flutter/android/build.gradle @@ -64,8 +64,7 @@ android { dependencies { testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'org.mockito:mockito-core:5.0.0' - // + Version 3.51.0 and the versions up to 4.0.0, not including 4.0.0 and higher - implementation 'com.posthog:posthog-android:[3.54.0,4.0.0]' + implementation 'com.posthog:posthog-android:[3.55.0,4.0.0)' } testOptions { From 73cc865769a31a959ce44d0e9d68d53edafd9ce1 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Thu, 16 Jul 2026 12:03:56 +0300 Subject: [PATCH 08/13] test: cover the Android bootstrap config decode --- .../posthog/flutter/PosthogFlutterPlugin.kt | 20 ++++---- .../flutter/PosthogFlutterPluginTest.kt | 51 ++++++++++++++++++- 2 files changed, 60 insertions(+), 11 deletions(-) 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 63476329..d34a7e60 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 @@ -490,15 +490,8 @@ class PosthogFlutterPlugin : } // Bootstrap precedence and flag layering live in the native SDK; forward values only. - posthogConfig.getIfNotNull>("bootstrap") { bootstrap -> - @Suppress("UNCHECKED_CAST") - this.bootstrap = - PostHogBootstrapConfig( - distinctId = bootstrap["distinctId"] as? String, - isIdentifiedId = bootstrap["isIdentifiedId"] as? Boolean ?: false, - featureFlags = bootstrap["featureFlags"] as? Map, - featureFlagPayloads = bootstrap["featureFlagPayloads"] as? Map, - ) + posthogConfig.getIfNotNull>("bootstrap") { + this.bootstrap = bootstrapConfigFromMap(it) } sdkName = "posthog-flutter" @@ -1437,3 +1430,12 @@ class PosthogFlutterPlugin : flutterSurveysDelegate?.handleSurveyAction(type, args, result) } } + +@Suppress("UNCHECKED_CAST") +internal fun bootstrapConfigFromMap(bootstrap: Map): PostHogBootstrapConfig = + PostHogBootstrapConfig( + distinctId = bootstrap["distinctId"] as? String, + isIdentifiedId = bootstrap["isIdentifiedId"] as? Boolean ?: false, + featureFlags = bootstrap["featureFlags"] as? Map, + featureFlagPayloads = bootstrap["featureFlagPayloads"] as? Map, + ) diff --git a/posthog_flutter/android/src/test/kotlin/com/posthog/flutter/PosthogFlutterPluginTest.kt b/posthog_flutter/android/src/test/kotlin/com/posthog/flutter/PosthogFlutterPluginTest.kt index 5822b829..1e4ef8bf 100644 --- a/posthog_flutter/android/src/test/kotlin/com/posthog/flutter/PosthogFlutterPluginTest.kt +++ b/posthog_flutter/android/src/test/kotlin/com/posthog/flutter/PosthogFlutterPluginTest.kt @@ -4,10 +4,12 @@ import android.app.Activity import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull import org.mockito.Mockito import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue /* * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. @@ -189,4 +191,49 @@ internal class PosthogFlutterPluginTest { Mockito.isNull(), ) } + + @Test + fun bootstrapConfigFromMap_fullMap_decodesAllFields() { + val config = + bootstrapConfigFromMap( + mapOf( + "distinctId" to "user-123", + "isIdentifiedId" to true, + "featureFlags" to mapOf("beta-ui" to "variant-a", "legacy" to true), + "featureFlagPayloads" to mapOf("beta-ui" to mapOf("color" to "blue")), + ), + ) + + assertEquals("user-123", config.distinctId) + assertTrue(config.isIdentifiedId) + assertEquals(mapOf("beta-ui" to "variant-a", "legacy" to true), config.featureFlags) + assertEquals(mapOf("beta-ui" to mapOf("color" to "blue")), config.featureFlagPayloads) + } + + @Test + fun bootstrapConfigFromMap_emptyMap_usesDefaults() { + val config = bootstrapConfigFromMap(emptyMap()) + + assertNull(config.distinctId) + assertFalse(config.isIdentifiedId) + assertNull(config.featureFlags) + assertNull(config.featureFlagPayloads) + } + + @Test + fun bootstrapConfigFromMap_wrongTypes_fallBackToDefaults() { + val config = + bootstrapConfigFromMap( + mapOf( + "distinctId" to 42, + "isIdentifiedId" to "yes", + "featureFlags" to listOf("beta-ui"), + ), + ) + + assertNull(config.distinctId) + assertFalse(config.isIdentifiedId) + assertNull(config.featureFlags) + assertNull(config.featureFlagPayloads) + } } From bdf16c79f2ccc1adbdbac6a27f2a3f59e1669249 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Thu, 16 Jul 2026 12:42:22 +0300 Subject: [PATCH 09/13] refactor: type the bootstrap channel map as Map --- api/posthog_flutter.api.json | 2 +- posthog_flutter/lib/src/posthog_config.dart | 2 +- posthog_flutter/test/posthog_test.dart | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index 3584ba52..7775545a 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -2789,7 +2789,7 @@ "name": "toMap", "parameters": [], "relativePath": "lib/src/posthog_config.dart", - "returnTypeName": "Map", + "returnTypeName": "Map", "type": "method", "typeParameterNames": [] }, diff --git a/posthog_flutter/lib/src/posthog_config.dart b/posthog_flutter/lib/src/posthog_config.dart index d55f34d9..b20f266b 100644 --- a/posthog_flutter/lib/src/posthog_config.dart +++ b/posthog_flutter/lib/src/posthog_config.dart @@ -385,7 +385,7 @@ class 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() { + Map toMap() { final flags = featureFlags; if (flags != null) { for (final entry in flags.entries) { diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index ae3fae74..b8d8a5aa 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -172,7 +172,7 @@ void main() { isIdentifiedId: true, ); - final bootstrap = config.toMap()['bootstrap'] as Map; + final bootstrap = config.toMap()['bootstrap'] as Map; expect(bootstrap['distinctId'], equals('user-123')); expect(bootstrap['isIdentifiedId'], isTrue); expect(bootstrap.containsKey('featureFlags'), isFalse); @@ -183,7 +183,7 @@ void main() { final config = PostHogConfig('test_project_token') ..bootstrap = const PostHogBootstrapConfig(distinctId: 'anon-abc'); - final bootstrap = config.toMap()['bootstrap'] as Map; + final bootstrap = config.toMap()['bootstrap'] as Map; expect(bootstrap['distinctId'], equals('anon-abc')); expect(bootstrap['isIdentifiedId'], isFalse); }); @@ -197,7 +197,7 @@ void main() { }, ); - final bootstrap = config.toMap()['bootstrap'] as Map; + final bootstrap = config.toMap()['bootstrap'] as Map; expect(bootstrap.containsKey('distinctId'), isFalse); expect(bootstrap['isIdentifiedId'], isFalse); expect( @@ -228,7 +228,7 @@ void main() { ); // The mismatched value is still forwarded; the native SDK drops it. - final bootstrap = map['bootstrap'] as Map; + final bootstrap = map['bootstrap'] as Map; expect( bootstrap['featureFlags'], equals({'discount-tier': 2, 'beta-ui': 'variant-a'}), From b1119a9e7c844b17842e0820c87461de2034b8a9 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Thu, 16 Jul 2026 13:21:26 +0300 Subject: [PATCH 10/13] chore: wrap single-line if bodies for swiftformat 0.62 --- .../Sources/posthog_flutter/PosthogFlutterPlugin.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 d5e56665..0fc1f9a6 100644 --- a/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift +++ b/posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift @@ -625,10 +625,14 @@ extension PosthogFlutterPlugin { if let webView = view as? WKWebView { let frameInWindow = webView.convert(webView.bounds, to: nil) // 1pt slack absorbs rounding between Flutter's rect and the native frame. - if rect.insetBy(dx: -1, dy: -1).contains(frameInWindow) { return webView } + if rect.insetBy(dx: -1, dy: -1).contains(frameInWindow) { + return webView + } } for sub in view.subviews { - if let found = findWKWebView(in: sub, containedBy: rect) { return found } + if let found = findWKWebView(in: sub, containedBy: rect) { + return found + } } return nil } From d8a5d84aeda9a868476250a17004229e86ea66cf Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Thu, 16 Jul 2026 18:53:27 +0300 Subject: [PATCH 11/13] docs: fix the stale 3.61.0 version comment in the podspec --- posthog_flutter/darwin/posthog_flutter.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog_flutter/darwin/posthog_flutter.podspec b/posthog_flutter/darwin/posthog_flutter.podspec index d85c0bc3..4458d19d 100644 --- a/posthog_flutter/darwin/posthog_flutter.podspec +++ b/posthog_flutter/darwin/posthog_flutter.podspec @@ -21,7 +21,7 @@ Postog flutter plugin s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' - # ~> Version 3.61.0 up to, but not including, 4.0.0 + # ~> Version 3.66.0 up to, but not including, 4.0.0 s.dependency 'PostHog', '>= 3.66.0', '< 4.0.0' s.ios.deployment_target = '13.0' From b23b26064bb21c73a24a61c86943092c9ef7181e Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Thu, 16 Jul 2026 18:53:31 +0300 Subject: [PATCH 12/13] fix(bootstrap): allow null bootstrap featureFlagPayloads values --- api/posthog_flutter.api.json | 4 ++-- posthog_flutter/lib/src/posthog_config.dart | 4 ++-- posthog_flutter/test/posthog_test.dart | 2 ++ sdk_compliance_adapter/lib/adapter_server.dart | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/api/posthog_flutter.api.json b/api/posthog_flutter.api.json index 7775545a..9b55fe23 100644 --- a/api/posthog_flutter.api.json +++ b/api/posthog_flutter.api.json @@ -2834,7 +2834,7 @@ "isRequired": false, "name": "featureFlagPayloads", "relativePath": "lib/src/posthog_config.dart", - "typeName": "Map?" + "typeName": "Map?" } ], "relativePath": "lib/src/posthog_config.dart", @@ -2886,7 +2886,7 @@ "isWriteable": false, "name": "featureFlagPayloads", "relativePath": "lib/src/posthog_config.dart", - "typeName": "Map?" + "typeName": "Map?" } ], "isDeprecated": false, diff --git a/posthog_flutter/lib/src/posthog_config.dart b/posthog_flutter/lib/src/posthog_config.dart index b20f266b..17ef682f 100644 --- a/posthog_flutter/lib/src/posthog_config.dart +++ b/posthog_flutter/lib/src/posthog_config.dart @@ -378,8 +378,8 @@ class PostHogBootstrapConfig { final Map? featureFlags; /// JSON payloads paired with [featureFlags], keyed by flag key. Each value is - /// the already-decoded payload (map, list, string, number, ...). - final Map? featureFlagPayloads; + /// the already-decoded payload (map, list, string, number, `null`, ...). + final Map? featureFlagPayloads; /// Converts this configuration to a platform-channel map. /// diff --git a/posthog_flutter/test/posthog_test.dart b/posthog_flutter/test/posthog_test.dart index b8d8a5aa..bb829a1e 100644 --- a/posthog_flutter/test/posthog_test.dart +++ b/posthog_flutter/test/posthog_test.dart @@ -194,6 +194,7 @@ void main() { featureFlags: {'beta-ui': 'variant-a', 'legacy': true}, featureFlagPayloads: { 'beta-ui': {'color': 'blue'}, + 'legacy': null, }, ); @@ -208,6 +209,7 @@ void main() { bootstrap['featureFlagPayloads'], equals({ 'beta-ui': {'color': 'blue'}, + 'legacy': null, }), ); }); diff --git a/sdk_compliance_adapter/lib/adapter_server.dart b/sdk_compliance_adapter/lib/adapter_server.dart index 97519fac..3af81bd3 100644 --- a/sdk_compliance_adapter/lib/adapter_server.dart +++ b/sdk_compliance_adapter/lib/adapter_server.dart @@ -105,7 +105,7 @@ class ComplianceAdapter { isIdentifiedId: bootstrap['is_identified_id'] == true, featureFlags: _readObjectMap(bootstrap['feature_flags']), featureFlagPayloads: - _readObjectMap(bootstrap['feature_flag_payloads']), + _readNullableObjectMap(bootstrap['feature_flag_payloads']), ); } From 96a45743c565e9a610184e95bc0ef26e4392224a Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Thu, 16 Jul 2026 18:53:31 +0300 Subject: [PATCH 13/13] chore: rename the android dev override so gradle compiles it against Settings --- CONTRIBUTING.md | 2 +- example/android/.gitignore | 2 +- example/android/settings.gradle.kts | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 288b3146..0c8677af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,7 +63,7 @@ changes. ### Android -- Create `example/android/settings.local.gradle.kts` (gitignored) with a composite +- Create `example/android/local.settings.gradle.kts` (gitignored) with a composite build of your checkout: ```kotlin diff --git a/example/android/.gitignore b/example/android/.gitignore index 7d35ea3d..3fa29020 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -7,7 +7,7 @@ gradle-wrapper.jar GeneratedPluginRegistrant.java # Local development override for an unreleased posthog-android (see CONTRIBUTING.md). -settings.local.gradle.kts +local.settings.gradle.kts # Remember to never publicly share your keystore. # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts index 0f180cf7..216f9238 100644 --- a/example/android/settings.gradle.kts +++ b/example/android/settings.gradle.kts @@ -26,6 +26,7 @@ plugins { include(":app") // Local development against an unreleased posthog-android. Inert unless a -// gitignored settings.local.gradle.kts exists; see CONTRIBUTING.md. -val localSettings = file("settings.local.gradle.kts") +// gitignored local.settings.gradle.kts exists; see CONTRIBUTING.md. The name must +// end in .settings.gradle.kts so Kotlin DSL compiles it against Settings. +val localSettings = file("local.settings.gradle.kts") if (localSettings.exists()) apply(from = localSettings)