liquid_posthog 0.2.0 copy "liquid_posthog: ^0.2.0" to clipboard
liquid_posthog: ^0.2.0 copied to clipboard

PostHog (product analytics, session replay, feature flags) destination for liquid_analytics.

liquid_posthog #

PostHog destination for liquid_analytics — product analytics, session replay, and feature flags, with an open-source/self-host option.

Install #

dependencies:
  liquid_analytics: ^0.2.0
  liquid_posthog: ^0.2.0

Use #

Let the sink configure PostHog for you by passing an apiKey:

final liquid = Liquid(
  sinks: [
    PostHogSink(
      apiKey: 'phc_xxx',
      host: 'https://eu.i.posthog.com',
      configure: (config) => config
        ..debug = true
        ..captureApplicationLifecycleEvents = true,
    ),
  ],
  consent: ConsentPolicy(
    requireOptIn: true,
    map: {ConsentCategory.analytics: ['posthog']},
  ),
);
await liquid.init();

liquid.track('checkout_started', {
  'cart_value': 42.0,
  'is_gift': true,             // real booleans are preserved
  'items': ['sku_1', 'sku_2'], // nested lists/maps are supported
});
liquid.consent.grant(ConsentCategory.analytics);

If PostHog is already initialized natively or via Posthog().setup(...), omit apiKey and the sink will use the existing instance.

The string API above is the quick path. A typed event keeps the name and property keys in one place, and PostHog preserves the nested shapes such an event naturally produces:

class CheckoutStarted extends LiquidEvent {
  const CheckoutStarted({required this.cartValue, required this.items});
  final double cartValue;
  final List<String> items;

  @override
  String get name => 'checkout_started';

  @override
  Map<String, Object?> get properties => {
        'cart_value': cartValue,
        'items': items, // lists and nested maps reach PostHog intact
      };
}

liquid.log(const CheckoutStarted(cartValue: 42, items: ['sku_1', 'sku_2']));

Override category to route an event to a different consent bucket:

class PromoTapped extends LiquidEvent {
  const PromoTapped({required this.campaign});
  final String campaign;

  @override
  String get name => 'promo_tapped';

  @override
  ConsentCategory get category => ConsentCategory.marketing;

  @override
  Map<String, Object?> get properties => {'campaign': campaign};
}

No build step is involved — these are plain classes. To generate them from a YAML schema instead, see liquid_codegen.

Message mapping #

Unlike Firebase, PostHog models liquid's full surface natively:

liquid message PostHog call
track capture
screen screen
identify identify
group group
alias alias
reset reset

PostHog's group uses a (groupType, groupKey) model. liquid's single-id group('acme') maps groupKey = 'acme' with the type set by defaultGroupType ('company' by default):

PostHogSink(apiKey: '…', defaultGroupType: 'organization');

Property handling #

PostHog accepts far more than Firebase — event names are unconstrained and properties may be nested objects, lists, booleans and numbers. PostHogPropertyMapper therefore only:

  • drops null values (PostHog's API is non-nullable), and
  • converts the couple of Dart types the platform channel cannot serialize — DateTime (to ISO-8601) and Enum (to .name) — recursively through nested maps and lists.

This is a deliberate contrast with liquid_firebase, whose mapper must flatten and sanitize aggressively. Each adapter owns the constraints of its provider so your app code never has to.

Testing #

PostHogPropertyMapper is a pure transform and is unit-tested without a live project. For end-to-end checks, run against a PostHog project (cloud or self-hosted) and watch the activity feed.

0
likes
160
points
24
downloads

Documentation

API reference

Publisher

verified publisherketok.id

Weekly Downloads

PostHog (product analytics, session replay, feature flags) destination for liquid_analytics.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#analytics #posthog #flutter

License

(pending) (license)

Dependencies

flutter, liquid_analytics, meta, posthog_flutter

More

Packages that depend on liquid_posthog