liquid_analytics 0.2.2
liquid_analytics: ^0.2.2 copied to clipboard
Vendor-neutral analytics for Flutter. Emit one typed event and fan it out to any set of providers through a middleware pipeline and a built-in consent gate.
// Core liquid_analytics usage: one typed event, many sinks, consent-gated.
//
// Run with any sinks you like — this example uses the built-in DebugSink and
// FakeSink so it needs no provider credentials.
// ignore_for_file: avoid_print
import 'package:liquid_analytics/liquid_analytics.dart';
/// A typed event. The name and property keys live in one place, with no
/// code-generation step.
class CheckoutStarted extends LiquidEvent {
const CheckoutStarted({required this.cartValue, this.itemCount = 1});
final double cartValue;
final int itemCount;
@override
String get name => 'checkout_started';
@override
Map<String, Object?> get properties => {
'cart_value': cartValue,
'item_count': itemCount,
};
}
/// Marketing events declare their own consent category.
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};
}
Future<void> main() async {
final product = FakeSink(id: 'product');
final ads = FakeSink(id: 'ads');
final liquid = Liquid(
sinks: [DebugSink(), product, ads],
middleware: [
GlobalContext({'app': 'example', 'env': 'dev'}),
PiiRedactor(fields: {'email'}),
],
consent: const ConsentPolicy(
requireOptIn: true, // nothing leaves the device until the user opts in
map: {
ConsentCategory.analytics: ['debug', 'product'],
ConsentCategory.marketing: ['ads'],
},
),
);
await liquid.init();
// Emitted before a decision: held in the consent buffer, not sent.
liquid.log(const CheckoutStarted(cartValue: 42, itemCount: 3));
liquid.log(const PromoTapped(campaign: 'summer_sale'));
print('buffered before consent: ${liquid.bufferedCount}');
// The user accepts analytics only. Buffered analytics events replay now;
// the marketing event stays held until that category is decided too.
liquid.consent.grant(ConsentCategory.analytics);
await liquid.flush();
print('product sink received: ${product.tracks.map((m) => m.name).toList()}');
print('ads sink received: ${ads.tracks.map((m) => m.name).toList()}');
// Identity and screens use the same client.
liquid.identify('user_123', {'plan': 'pro', 'email': 'a@b.com'});
liquid.screen('Cart');
await liquid.flush();
// Revoking consent stops delivery immediately — including anything still
// sitting in the offline queue.
liquid.consent.deny(ConsentCategory.analytics);
await liquid.reset(); // clears the user across every sink
await liquid.dispose();
}