Flutter SDK integration and tracking setup
Installation
Section titled “Installation”https://pub.dev/packages/marfeel_sdk
Add the marfeel_sdk package to your pubspec.yaml:
dependencies: marfeel_sdk: ^0.1.0Then run:
flutter pub getAndroid setup
Section titled “Android setup”Add the following repository to your app-level android/build.gradle file:
allprojects { repositories { google() mavenCentral() maven { url "https://repositories.mrf.io/nexus/repository/mvn-marfeel-public/" } }}iOS setup
Section titled “iOS setup”No additional setup is required. The native SDK is installed automatically via CocoaPods.
To begin tracking, initialize the SDK using the initialize method and provide your unique Marfeel Account Id:
import 'package:marfeel_sdk/marfeel_sdk.dart';
CompassTracking.initialize('/* AccountId */');/* AccountId */ placeholder with your Marfeel Account ID.
Page Technology
Section titled “Page Technology”The default value for the Page Technology dimension depends on the underlying platform (iOS App or Android App). However, you can modify this default value using the pageTechnology parameter:
CompassTracking.initialize('/* AccountId */', pageTechnology: 105);To utilize the library, import the marfeel_sdk package.
import 'package:marfeel_sdk/marfeel_sdk.dart';All tracking features are available as static methods on the CompassTracking class.
Page Tracking
Section titled “Page Tracking”CompassTracking automatically monitors user time on a page. When a user begins reading a new page, you should provide the page’s canonical URL.
CompassTracking.trackNewPage('https://example.com/article');CompassTracking.trackNewPage('https://example.com/article', rs: 'homepage');In cases where you can’t provide the canonical URL you can use the Editorial Metadata API.
CompassTracking will continue tracking reading time until the trackNewPage method is called again with a different URL or until you explicitly invoke the stopTracking() method.
CompassTracking.stopTracking();If you want to track a screen instead of a new page, particularly when your view doesn’t have a real URI, you can utilize the trackScreen method. This method accepts a raw name instead of an actual URI for tracking purposes.
CompassTracking.trackScreen('profile');CompassTracking.trackScreen('profile', rs: 'menu');Landing Page Tracking
Section titled “Landing Page Tracking”If you want to inform about the landing page of the session, use the following method:
CompassTracking.setLandingPage('https://example.com/');If you want to include UTMs, you’ll need to add them to the landing page.
Scroll Tracking
Section titled “Scroll Tracking”The SDK provides CompassScrollView, a widget that wraps SingleChildScrollView and automatically tracks scroll depth:
CompassScrollView( child: Column( children: [ // Your scrollable content ], ),)CompassScrollView supports the same properties as SingleChildScrollView:
CompassScrollView( controller: myScrollController, padding: EdgeInsets.all(16), physics: BouncingScrollPhysics(), scrollDirection: Axis.vertical, child: Column( children: [ // Your scrollable content ], ),)You can also report scroll percentage manually:
CompassTracking.updateScrollPercentage(75);User Identification
Section titled “User Identification”To associate the app user to the records generated by the library, use the setSiteUserId method, indicating the user ID in your platform.
CompassTracking.setSiteUserId('user_123');On the other hand, if you need to retrieve marfeel user id for forwarding it to one of our apis, the method you need to use is getUserId()
final userId = await CompassTracking.getUserId();User Journey
Section titled “User Journey”You can specify the type of user with the options below. By default, if not specified, the user will be tracked as Anonymous.
CompassTracking.setUserType(UserType.anonymous);CompassTracking.setUserType(UserType.logged);CompassTracking.setUserType(UserType.paid);CompassTracking.setUserType(UserType.custom(8));It's recommended to first provide the user ID and the user journey information before initiating the tracking of the first pageview.
User RFV
Section titled “User RFV”If you want to access the RFV of the user within a session, you can retrieve it using the getRFV method.
final rfv = await CompassTracking.getRFV();if (rfv != null) { // RFV object containing: // - rfv (double): The composite RFV score representing overall user engagement. // - r (double): Recency — how recently the user has visited. // - f (double): Frequency — how often the user visits. // - v (double): Volume — how much content the user consumes.}Conversion Tracking
Section titled “Conversion Tracking”You can track conversions calling the trackConversion() method:
CompassTracking.trackConversion('subscription');Conversion parameters
Section titled “Conversion parameters”Just like in web, Conversion parameters are available for Native
CompassTracking.trackConversion( 'purchase', options: ConversionOptions( id: 'order_123', // each pair {CONVERSION} and {ID} will only be tracked once value: '29.99', meta: {'currency': 'EUR', 'plan': 'annual'}, scope: ConversionScope.page, // page | session | user ),);Keep in mind that trackConversion with the same conversion and options.id will only be tracked once.
In the following example, if you send the first call:
CompassTracking.trackConversion( 'conv_1', options: ConversionOptions( id: 'id_1', value: '3', ),);A second call won’t be tracked because conv_1:id_1 has already been tracked:
CompassTracking.trackConversion( 'conv_1', options: ConversionOptions( id: 'id_1', value: '4', ),);Consent Tracking
Section titled “Consent Tracking”In order to mark if the current visit has consent approved or not, sdk offers the following method to use:
CompassTracking.setConsent(true);User data will be deleted each time the user closes the app if no consent is provided.
Custom Dimensions
Section titled “Custom Dimensions”Page vars
Section titled “Page vars”CompassTracking.setPageVar('category', 'technology');Session vars
Section titled “Session vars”CompassTracking.setSessionVar('theme', 'dark');User vars
Section titled “User vars”CompassTracking.setUserVar('preferredLanguage', 'en');User segments
Section titled “User segments”Adds a new persistent user segment
CompassTracking.addUserSegment('premium');Replaces existing user segments
CompassTracking.setUserSegments(['premium', 'newsletter']);Removes existing user segment
CompassTracking.removeUserSegment('newsletter');Clears all user segments
CompassTracking.clearUserSegments();Custom Metrics
Section titled “Custom Metrics”Page metrics
Section titled “Page metrics”CompassTracking.setPageMetric('wordCount', 1200);Multimedia Tracking
Section titled “Multimedia Tracking”All multimedia tracking features are available as static methods on the MultimediaTracking class.
Exposed interfaces
Section titled “Exposed interfaces”class MultimediaTracking { static void initializeItem({ required String id, required String provider, required String providerId, required MultimediaType type, MultimediaMetadata? metadata, });
static void registerEvent({ required String id, required MultimediaEvent event, required int eventTime, });}
enum MultimediaType { audio, video,}
enum MultimediaEvent { play, pause, end, updateCurrentTime, adPlay, mute, unmute, fullScreen, backScreen, enterViewport, leaveViewport,}
class MultimediaMetadata { final bool? isLive; final String? title; final String? description; final String? url; final String? thumbnail; final String? authors; final int? publishTime; final int? duration;}For the full list of available events (
MultimediaEvent) and media types (MultimediaType), refer to the Multimedia tracking documentation.
To initialize a new media
Section titled “To initialize a new media”MultimediaTracking.initializeItem( id: 'videoId', provider: 'youtube', providerId: 'youtube-video-id', type: MultimediaType.video, metadata: MultimediaMetadata( title: 'Video Title', description: 'Video description', url: 'https://youtube.com/watch?v=youtube-video-id', authors: 'John Doe', duration: 420, ),);To track a new event
Section titled “To track a new event”MultimediaTracking.registerEvent( id: 'videoId', event: MultimediaEvent.updateCurrentTime, eventTime: 150,);Recirculation Tracking
Section titled “Recirculation Tracking”Track which elements in your app are shown, visible, and clicked. Works for any element you need to measure: recommendation modules, banners, CTAs, or anything else. The SDK reports eligibility, visibility, and clicks; your app decides what those elements are.
import 'package:marfeel_sdk/marfeel_sdk.dart';experiences_screen.dart in the demo app for a complete recirculation tracking flow.
RecirculationLink
Section titled “RecirculationLink”Represents a single trackable element within a module:
| Property | Type | Description |
|---|---|---|
url | String | Canonical URL of the element’s destination |
position | int | Zero-based position of the element within the module |
class RecirculationLink { final String url; final int position;
const RecirculationLink({required this.url, required this.position});}Tracking lifecycle
Section titled “Tracking lifecycle”Each module must be tracked through its full lifecycle:
| Method | When to call |
|---|---|
trackEligible | The module has been selected and will appear in the current page view |
trackImpression / trackImpressionLink | One or more elements become visible on screen |
trackClick | The user taps an element within the module |
const moduleName = 'example-module-name';const links = [ RecirculationLink(url: 'https://example.com/article-1', position: 0), RecirculationLink(url: 'https://example.com/article-2', position: 1), RecirculationLink(url: 'https://example.com/article-3', position: 2),];
Recirculation.trackEligible(name: moduleName, links: links);Recirculation.trackImpression(name: moduleName, links: [links[0], links[1]]);Recirculation.trackImpressionLink(name: moduleName, link: links[2]);Recirculation.trackClick(name: moduleName, link: links[1]);Experiences
Section titled “Experiences”The Experiences API lets you fetch experiences configured in Experience Manager for the current page so your app can render them. You can filter by type or family, resolve remote content on demand, and track user interactions. The SDK returns the configuration; rendering is the app’s responsibility.
All experiences features are exposed as static methods on the Experiences class:
import 'package:marfeel_sdk/marfeel_sdk.dart';experiences_screen.dart in the demo app for a complete experiences fetch and tracking flow.
Fetching Experiences
Section titled “Fetching Experiences”Fetch experiences for the current targeting using fetchExperiences. The SDK automatically uses the URL from the active page being tracked. This is an asynchronous function that returns a Future<List<Experience>>.
final result = await Experiences.fetchExperiences();fetchExperiences requires the SDK to be initialized via CompassTracking.initialize() and a page to be actively tracked via trackNewPage or trackScreen. If no page is being tracked, an empty list is returned.
Each Experience contains:
| Property | Type | Description |
|---|---|---|
id | String | Unique experience identifier |
name | String | Experience name from the server configuration |
type | ExperienceType | Experience type (inline, flowcards, compass, adManager, etc.) |
family | ExperienceFamily? | Optional experience family (recommender, paywall, widget, etc.). Null when not specified by the server |
placement | String? | Placement identifier |
contentUrl | String? | URL to fetch the experience content from |
contentType | ExperienceContentType | Content type (textHTML, json, amp, etc.) |
features | Map<String, dynamic>? | Arbitrary feature flags and configuration |
strategy | String? | Placement strategy (e.g., replace, append) |
resolvedContent | String? | The fetched content body, populated after calling Experiences.resolveExperience() or when resolve = true |
rawJson | Map<String, dynamic> | The full original JSON for accessing fields not explicitly modeled |
Filtering
Section titled “Filtering”You can filter experiences by type, family, or both:
// Filter by typefinal inlineOnly = await Experiences.fetchExperiences( filterByType: ExperienceType.inline,);
// Filter by familyfinal recommenders = await Experiences.fetchExperiences( filterByFamily: ExperienceFamily.recommender,);
// Combine both filters (AND logic)final inlineRecommenders = await Experiences.fetchExperiences( filterByType: ExperienceType.inline, filterByFamily: ExperienceFamily.recommender,);Available experience families:
| Enum Value | Server Key |
|---|---|
twitter | twitterexperience |
facebook | facebookexperience |
youtube | youtubeexperience |
recommender | recommenderexperience |
telegram | telegramexperience |
gathering | gatheringexperience |
affiliate | affiliateexperience |
podcast | podcastexperience |
experimentation | experimentsexperience |
widget | widgetexperience |
marfeelPass | passexperience |
script | scriptexperience |
paywall | paywallexperience |
marfeelSocial | marfeelsocial |
family field. Use it when accessing rawJson directly or when debugging API responses. When the server returns an unrecognized value, it maps to ExperienceFamily.unknown. When the field is absent, it is null.
Content Resolution
Section titled “Content Resolution”By default, fetchExperiences does not resolve content. You can enable automatic resolution for all experiences that have a contentUrl, or resolve individually on demand:
// No content resolution (default)final result = await Experiences.fetchExperiences();result.first.resolvedContent; // null
// Resolve all content automaticallyfinal resolvedResult = await Experiences.fetchExperiences(resolve: true);resolvedResult.first.resolvedContent; // contains the fetched content
// Resolve individually on demandfinal lazyResult = await Experiences.fetchExperiences();await Experiences.resolveExperience(lazyResult.first);Custom Targeting
Section titled “Custom Targeting”Add custom targeting key-value pairs that will be sent with every experiences request:
Experiences.addTargeting('key', 'value');Tracking
Section titled “Tracking”Each experience that your app consumes must be tracked through its full lifecycle. Call the appropriate method as each stage occurs. Tracking link interactions uses RecirculationLink, defined in the Recirculation Tracking section above.
| Method | When to call |
|---|---|
trackEligible | The experience has been selected and is intended to be shown on the current page view |
trackImpression / trackImpressionLink | The experience content has been rendered and is visible to the user |
trackClick | The user tapped on a specific link within the experience |
trackClose | The user dismissed or closed the experience |
final result = await Experiences.fetchExperiences( filterByFamily: ExperienceFamily.recommender,);final recommender = result.first;
const links = [ RecirculationLink(url: 'https://example.com/article-1', position: 0), RecirculationLink(url: 'https://example.com/article-2', position: 1), RecirculationLink(url: 'https://example.com/article-3', position: 2),];
// The experience will be used in this page viewExperiences.trackEligible(experience: recommender, links: links);
// First two links are visible on screenExperiences.trackImpression( experience: recommender, links: [links[0], links[1]],);
// After scrolling, the third link becomes visibleExperiences.trackImpressionLink(experience: recommender, link: links[2]);
// The user tapped a link inside the experienceExperiences.trackClick(experience: recommender, link: links[1]);
// The user dismissed the experienceExperiences.trackClose(recommender);