Skip to content

Commit ea8e6ad

Browse files
authored
chore: Expose userProperties, userPropertiesSetOnce capture method (#254)
1 parent a9a2bc6 commit ea8e6ad

File tree

14 files changed

+447
-17
lines changed

14 files changed

+447
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Next
22

3+
- feat: add `userProperties` and `userPropertiesSetOnce` parameters to `capture()` method ([#254](https://github.com/PostHog/posthog-flutter/pull/254))
4+
35
# 5.11.1
46

57
- fix: RichText, SelectableText, TextField labels and hints not being masked in session replay ([#251](https://github.com/PostHog/posthog-flutter/pull/251))

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,14 @@ class PosthogFlutterPlugin :
389389
try {
390390
val eventName: String = call.argument("eventName")!!
391391
val properties: Map<String, Any>? = call.argument("properties")
392-
PostHog.capture(eventName, properties = properties)
392+
val userProperties: Map<String, Any>? = call.argument("userProperties")
393+
val userPropertiesSetOnce: Map<String, Any>? = call.argument("userPropertiesSetOnce")
394+
PostHog.capture(
395+
eventName,
396+
properties = properties,
397+
userProperties = userProperties,
398+
userPropertiesSetOnce = userPropertiesSetOnce,
399+
)
393400
result.success(null)
394401
} catch (e: Throwable) {
395402
result.error("PosthogFlutterException", e.localizedMessage, null)

example/lib/main.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Future<void> main() async {
88

99
WidgetsFlutterBinding.ensureInitialized();
1010
final config =
11-
PostHogConfig('phc_QFbR1y41s5sxnNTZoyKG2NJo2RlsCIWkUfdpawgb40D');
11+
PostHogConfig('phc_6lqCaCDCBEWdIGieihq5R2dZpPVbAUFISA75vFZow06');
1212
config.onFeatureFlags = () {
1313
debugPrint('[PostHog] Feature flags loaded!');
1414
};
@@ -127,6 +127,10 @@ class InitialScreenState extends State<InitialScreen> {
127127
_posthogFlutterPlugin
128128
.capture(eventName: "eventName", properties: {
129129
"foo": "bar",
130+
}, userProperties: {
131+
"user_foo": "user_bar",
132+
}, userPropertiesSetOnce: {
133+
"user_foo_once": "user_bar_once",
130134
});
131135
},
132136
child: const Text("Capture Event"),

example/web/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<script async>
3737
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
3838
posthog.init(
39-
'phc_QFbR1y41s5sxnNTZoyKG2NJo2RlsCIWkUfdpawgb40D',
39+
'phc_6lqCaCDCBEWdIGieihq5R2dZpPVbAUFISA75vFZow06',
4040
{
4141
api_host:'https://us.i.posthog.com',
4242
debug: true,

ios/Classes/PosthogFlutterPlugin.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,13 @@ extension PosthogFlutterPlugin {
560560
let eventName = args["eventName"] as? String
561561
{
562562
let properties = args["properties"] as? [String: Any]
563+
let userProperties = args["userProperties"] as? [String: Any]
564+
let userPropertiesSetOnce = args["userPropertiesSetOnce"] as? [String: Any]
563565
PostHogSDK.shared.capture(
564566
eventName,
565-
properties: properties
567+
properties: properties,
568+
userProperties: userProperties,
569+
userPropertiesSetOnce: userPropertiesSetOnce
566570
)
567571
result(nil)
568572
} else {

lib/posthog_flutter_web.dart

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
99
import 'src/posthog_config.dart';
1010
import 'src/posthog_flutter_platform_interface.dart';
1111
import 'src/posthog_flutter_web_handler.dart';
12+
import 'src/utils/capture_utils.dart';
13+
import 'src/utils/property_normalizer.dart';
1214

1315
/// A web implementation of the PosthogFlutterPlatform of the PosthogFlutter plugin.
1416
class PosthogFlutterWeb extends PosthogFlutterPlatformInterface {
@@ -89,10 +91,37 @@ class PosthogFlutterWeb extends PosthogFlutterPlatformInterface {
8991
Future<void> capture({
9092
required String eventName,
9193
Map<String, Object>? properties,
94+
Map<String, Object>? userProperties,
95+
Map<String, Object>? userPropertiesSetOnce,
9296
}) async {
97+
final extracted = CaptureUtils.extractUserProperties(
98+
properties: properties,
99+
userProperties: userProperties,
100+
userPropertiesSetOnce: userPropertiesSetOnce,
101+
);
102+
103+
final extractedProperties = extracted.properties;
104+
final extractedUserProperties = extracted.userProperties;
105+
final extractedUserPropertiesSetOnce = extracted.userPropertiesSetOnce;
106+
107+
final normalizedProperties = extractedProperties != null
108+
? PropertyNormalizer.normalize(extractedProperties)
109+
: null;
110+
final normalizedUserProperties = extractedUserProperties != null
111+
? PropertyNormalizer.normalize(extractedUserProperties)
112+
: null;
113+
final normalizedUserPropertiesSetOnce =
114+
extractedUserPropertiesSetOnce != null
115+
? PropertyNormalizer.normalize(extractedUserPropertiesSetOnce)
116+
: null;
117+
93118
return handleWebMethodCall(MethodCall('capture', {
94119
'eventName': eventName,
95-
if (properties != null) 'properties': properties,
120+
if (normalizedProperties != null) 'properties': normalizedProperties,
121+
if (normalizedUserProperties != null)
122+
'userProperties': normalizedUserProperties,
123+
if (normalizedUserPropertiesSetOnce != null)
124+
'userPropertiesSetOnce': normalizedUserPropertiesSetOnce,
96125
}));
97126
}
98127

lib/src/posthog.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class Posthog {
8383
Future<void> capture({
8484
required String eventName,
8585
Map<String, Object>? properties,
86+
Map<String, Object>? userProperties,
87+
Map<String, Object>? userPropertiesSetOnce,
8688
}) {
8789
final propertiesCopy = properties == null ? null : {...properties};
8890

@@ -95,6 +97,8 @@ class Posthog {
9597
return _posthog.capture(
9698
eventName: eventName,
9799
properties: propertiesCopy,
100+
userProperties: userProperties,
101+
userPropertiesSetOnce: userPropertiesSetOnce,
98102
);
99103
}
100104

lib/src/posthog_flutter_io.dart

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:posthog_flutter/src/util/logging.dart';
1010
import 'surveys/models/posthog_display_survey.dart' as models;
1111
import 'surveys/models/survey_callbacks.dart';
1212
import 'error_tracking/dart_exception_processor.dart';
13+
import 'utils/capture_utils.dart';
1314
import 'utils/property_normalizer.dart';
1415

1516
import 'posthog_config.dart';
@@ -175,18 +176,42 @@ class PosthogFlutterIO extends PosthogFlutterPlatformInterface {
175176
Future<void> capture({
176177
required String eventName,
177178
Map<String, Object>? properties,
179+
Map<String, Object>? userProperties,
180+
Map<String, Object>? userPropertiesSetOnce,
178181
}) async {
179182
if (!isSupportedPlatform()) {
180183
return;
181184
}
182185

183186
try {
184-
final normalizedProperties =
185-
properties != null ? PropertyNormalizer.normalize(properties) : null;
187+
final extracted = CaptureUtils.extractUserProperties(
188+
properties: properties,
189+
userProperties: userProperties,
190+
userPropertiesSetOnce: userPropertiesSetOnce,
191+
);
192+
193+
final extractedProperties = extracted.properties;
194+
final extractedUserProperties = extracted.userProperties;
195+
final extractedUserPropertiesSetOnce = extracted.userPropertiesSetOnce;
196+
197+
final normalizedProperties = extractedProperties != null
198+
? PropertyNormalizer.normalize(extractedProperties)
199+
: null;
200+
final normalizedUserProperties = extractedUserProperties != null
201+
? PropertyNormalizer.normalize(extractedUserProperties)
202+
: null;
203+
final normalizedUserPropertiesSetOnce =
204+
extractedUserPropertiesSetOnce != null
205+
? PropertyNormalizer.normalize(extractedUserPropertiesSetOnce)
206+
: null;
186207

187208
await _methodChannel.invokeMethod('capture', {
188209
'eventName': eventName,
189210
if (normalizedProperties != null) 'properties': normalizedProperties,
211+
if (normalizedUserProperties != null)
212+
'userProperties': normalizedUserProperties,
213+
if (normalizedUserPropertiesSetOnce != null)
214+
'userPropertiesSetOnce': normalizedUserPropertiesSetOnce,
190215
});
191216
} on PlatformException catch (exception) {
192217
printIfDebug('Exeption on capture: $exception');

lib/src/posthog_flutter_platform_interface.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ abstract class PosthogFlutterPlatformInterface extends PlatformInterface {
4444
Future<void> capture({
4545
required String eventName,
4646
Map<String, Object>? properties,
47+
Map<String, Object>? userProperties,
48+
Map<String, Object>? userPropertiesSetOnce,
4749
}) {
4850
throw UnimplementedError('capture() has not been implemented.');
4951
}

lib/src/posthog_flutter_web_handler.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class PostHog {}
1212
extension PostHogExtension on PostHog {
1313
external JSAny? identify(
1414
JSAny userId, JSAny properties, JSAny propertiesSetOnce);
15-
external JSAny? capture(JSAny eventName, JSAny properties);
15+
external JSAny? capture(JSAny eventName, JSAny? properties, JSAny? options);
1616
external JSAny? alias(JSAny alias);
1717
// ignore: non_constant_identifier_names
1818
external JSAny? get_distinct_id();
@@ -89,10 +89,24 @@ Future<dynamic> handleWebMethodCall(MethodCall call) async {
8989
case 'capture':
9090
final eventName = args['eventName'] as String;
9191
final properties = safeMapConversion(args['properties']);
92+
final userProperties = safeMapConversion(args['userProperties']);
93+
final userPropertiesSetOnce =
94+
safeMapConversion(args['userPropertiesSetOnce']);
95+
96+
// Build options object for posthog-js capture with $set and $set_once
97+
// See: https://github.com/PostHog/posthog-js/blob/main/packages/types/src/capture.ts
98+
final options = <String, Object>{};
99+
if (userProperties.isNotEmpty) {
100+
options['\$set'] = userProperties;
101+
}
102+
if (userPropertiesSetOnce.isNotEmpty) {
103+
options['\$set_once'] = userPropertiesSetOnce;
104+
}
92105

93106
posthog?.capture(
94107
stringToJSAny(eventName),
95-
mapToJSAny(properties),
108+
properties.isNotEmpty ? mapToJSAny(properties) : null,
109+
options.isNotEmpty ? mapToJSAny(options) : null,
96110
);
97111
break;
98112
case 'screen':
@@ -103,6 +117,7 @@ Future<dynamic> handleWebMethodCall(MethodCall call) async {
103117
posthog?.capture(
104118
stringToJSAny('\$screen'),
105119
mapToJSAny(properties),
120+
null,
106121
);
107122
break;
108123
case 'alias':

0 commit comments

Comments
 (0)