liquid_firebase 0.2.0
liquid_firebase: ^0.2.0 copied to clipboard
Firebase Analytics (Google Analytics for Firebase / GA4) destination for liquid_analytics.
liquid_firebase #
Firebase Analytics (Google Analytics for Firebase / GA4) destination for
liquid_analytics.
Install #
dependencies:
liquid_analytics: ^0.2.0
liquid_firebase: ^0.2.0
firebase_core: ^3.0.0
Use #
Initialize Firebase, then add FirebaseSink to your Liquid client:
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
final liquid = Liquid(
sinks: [FirebaseSink()],
consent: ConsentPolicy(
requireOptIn: true,
map: {ConsentCategory.analytics: ['firebase']},
),
);
await liquid.init();
liquid.track('checkout_started', {'cart_value': 42.0, 'is_gift': true});
liquid.consent.grant(ConsentCategory.analytics);
Typed events (recommended) #
The string API above is the quick path. A typed event keeps the name and
property keys in one place — and for Firebase specifically, it pins the
sanitizer-legal snake_case naming at the definition site instead of relying on
every call site to get it right:
class CheckoutStarted extends LiquidEvent {
const CheckoutStarted({required this.cartValue, this.isGift = false});
final double cartValue;
final bool isGift;
@override
String get name => 'checkout_started'; // already Firebase-legal
@override
Map<String, Object?> get properties => {
'cart_value': cartValue,
'is_gift': isGift, // encoded as 1/0 by FirebaseEventMapper
};
}
liquid.log(const CheckoutStarted(cartValue: 42, isGift: true));
No build step is involved — it is a plain class. Override category to route an
event to a different consent bucket (e.g. ConsentCategory.marketing). To
generate these from a YAML schema instead, see
liquid_codegen.
Message mapping #
| liquid message | Firebase call |
|---|---|
track |
logEvent |
screen |
logScreenView |
identify |
setUserId + setUserProperty per trait |
reset |
resetAnalyticsData |
Firebase has no group or alias concept, so those are ignored and the sink
does not advertise those capabilities (the client skips it for them).
Automatic sanitization #
Firebase rejects data that liquid's Map<String, Object?> allows: parameter
values must be a String or a number, names must be alphanumeric/underscore and
start with a letter, and there are length and count limits. FirebaseEventMapper
enforces all of this so events are never silently dropped by the native layer:
- Illegal name characters become underscores; names are truncated to 40 chars (24 for user properties) and forced to start with a letter.
- Booleans are encoded as
1/0(configurable), enums by.name,DateTimeas ISO-8601. Nested lists/maps are dropped. - String values are truncated to 100 chars; parameters are capped at 25.
Tune it by passing your own mapper:
FirebaseSink(mapper: const FirebaseEventMapper(boolsAsInts: false));
Firebase Consent Mode #
Optionally mirror liquid's consent into Firebase Consent Mode so Firebase itself honors the user's choice:
final firebaseSink = FirebaseSink();
final liquid = Liquid(sinks: [firebaseSink]);
liquid.consent.addListener(() {
firebaseSink.applyConsent(
analyticsGranted: liquid.consent.statusOf(ConsentCategory.analytics) ==
ConsentStatus.granted,
adsGranted: liquid.consent.statusOf(ConsentCategory.marketing) ==
ConsentStatus.granted,
);
});
Testing #
The sanitization logic lives in the pure FirebaseEventMapper, which is unit-
testable without a configured Firebase app. For end-to-end verification, run
your app against a real Firebase project and inspect DebugView.