Track screens

Contentsquare aggregate user behavior and engagement at the screen level. To leverage our full range of features, you must implement effective screen tracking.

You can set this up:

  • Manually, by calling a dedicated API.
  • Automatically, by using our observer to track screens based on changes in the navigation stack.

Call the .send(String) API when a screen appears to track screens manually. When you call the API, the SDK logs a screenview event which identifies the new screen with the screen name you provided.

ContentSquare().send('ScreenName');

Back navigation is not automatically tracked by the SDK. When back navigation occurs the send method should be manually invoked with the screen name of the previous screen.

Example with a custom navigation observer

Section titled Example with a custom navigation observer
class CustomNavigationObserver extends NavigatorObserver {
@override
void didPop(Route route, Route? previousRoute) {
...
final previousScreenName = // get the previous screen name
Contentsquare().send(previousScreenName);
...
}
}

The screen name length is not limited on the SDK side. However, it’s important to note that there is a limit of 2083 characters on the server side. Ensure that screen names do not exceed this limit to prevent any issues with data transmission and processing.

Handling Screen Views When App Returns from Background

Section titled Handling Screen Views When App Returns from Background

The Contentsquare SDK automatically triggers a screen view when the app is moved to the background and subsequently brought back to the foreground. This occurs provided that a screen view with a screen name has been triggered previously. In such cases, the SDK will use the last screen name that was set.

Implementation recommendations

Section titled Implementation recommendations

From a functional standpoint, we expect a screenview to be sent:

  • When the screen becomes visible
  • When a modal or pop-up is closed, returning the user to the screen
  • When a route is popped, returning the user to the screen

Achieving this can be facilitated through a NavigatorObserver. This observer can be used to listen to route changes and send screenviews accordingly.

As a general rule, aim to keep distinct screen names under 100 characters. Choose names that provide a comprehensive understanding of the screen’s content or purpose. This will help you to identify the screen in the Contentsquare interface.

Separate words with space, dash or underscore characters

Section titled Separate words with space, dash or underscore characters

If you want to create screen names with multiple words, it’s best to separate them using spaces, dashes, or underscores. Contentsquare will handle the formatting automatically.

Example: For a sub-category list of a retail app, use Home and Living - Home Furnishings instead of homeLivingHomeFurnishings.

Use screen template/layout names

Section titled Use screen template/layout names

As a general guideline, opt for names that describe the screen template or layout rather than focusing on specific content (data). This approach will minimize the number of unique screen names, making Contentsquare easier to use and reducing the potential for Personal Data transmission.

Examples of screen types falling into this category include Product Detail, Event Detail, Conversation/Chat, and User Profile.

Screens with multiple states/layouts

Section titled Screens with multiple states/layouts

Screens can have different layouts or states depending on the user context. In this case, append its value to the screen name.

Home screen

StateScreen name
App landing layoutHome
Women products layoutHome - Women
Men products layoutHome - Men
Sales layoutHome - Sales

Product Detail screen (PDP)

StateScreen name
Users on a Top for Women PDPPDP - Clothing - Women - Tops
Users on a Microwave PDPPDP - Kitchenware - Electrics - Microwave
Users on a Hotel details screenPDP - Holiday type - Season - Board

User account screen

StateScreen name
OverviewMy Account - Dashboard
Order historyMy Account - Order history
ReturnsMy Account - Returns

Search screen

StateScreen name
SearchSearch
Search results for “Skincare” productsSearch - Skincare
Search results error screenSearch - Error

Cart screen

StateScreen name
Empty cartCart - Empty
Items have been added to the cartCart - Populated
Issues with availability or pricingCart - Error

Checkout screen

StateScreen name
User provides name, surname, and date of birthCheckout - User Details
User provides shipping addressCheckout - Shipping Details
User inputs their credit card informationCheckout - Payment

Automatic screen view tracking can be done using the ContentsquareNavigatorObserver. This will observe the navigation stack of the application and log screen events accordingly.

This observer can be attached to any WidgetApp (like MaterialApp, CupertinoApp) or to a RouterConfig

MaterialApp(
navigatorObservers: [ContentsquareNavigatorObserver()],
);
CupertinoApp(
navigatorObservers: [ContentsquareNavigatorObserver()],
);
WidgetsApp(
navigatorObservers: [ContentsquareNavigatorObserver()],
);

The ContentsquareNavigatorObserver can be configured with the following arguments:

An optional callback function that determines whether a specific route should be tracked. The function takes a Route as input and returns a bool.

Use this function to selectively exclude certain routes from automatic tracking by returning false for those routes.

By default, all routes are tracked.

ContentsquareNavigatorObserver(
shouldTrack: (route) {
// Exclude /myRoute from tracking
if (route.settings.name == '/myRoute') return false;
return true;
},
)

An optional callback function used to customize the screen name for a given route. The function takes a Route as input and returns a String representing the desired screen name.

Use this function to define custom screen names for specific routes.

By default, the route’s name is used if available; otherwise, the route type will be used.

ContentsquareNavigatorObserver(
screenNameProvider: (route) {
final screenName = _formatScreenName(route.settings.name);
return screenName;
},
)

An optional callback function used to provide a list of custom variables for a specific route. The function takes a Route as input and returns a List<CustomVar>.

Use this function to attach custom variables to specific routes for tracking purposes.

ContentsquareNavigatorObserver(
customVarsProvider: (route) {
final routeName = route.settings.name;
final productPageRegex = RegExp(r'/product/(\d)+');
// In this example, a product ID is extracted from the route name.
// For a route like "/product/1234", the value "1234" will be set as a customVar.
if (routeName != null && productPageRegex.hasMatch(routeName)) {
final productId = productPageRegex.firstMatch(routeName)?.group(1);
if (productId != null) {
return [
CustomVar(index: 1, name: 'productId', value: productId),
];
}
}
return [];
},
)

TabBar views are commonly tracked as individual screens. However, most TabBar implementations push the route to the navigation stack only during initialization, which can make it challenging to properly track navigation events for each tab change.

To support TabBar tracking correctly, additional configuration may be necessary.

For example, when using AutoTabsRouter from the AutoRoute package, the default behavior does not track tab changes automatically. In this case, combining an AutoRouteObserver with the manual tracking API is required to ensure each tab is tracked as a distinct screen.

class TabRouteObserver extends AutoRouteObserver {
@override
void didInitTabRoute(TabPageRoute route, TabPageRoute? previousRoute) {
Contentsquare().send(route.name);
}
@override
void didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) {
Contentsquare().send(route.name);
}
@override
void didPop(Route route, Route? previousRoute) {
final routeName = previousRoute?.settings.name;
if (routeName == null) return;
if (_tabRouteNames.contains(routeName)) {
Contentsquare().send(routeName);
}
}
}
[...]
MaterialApp(
navigatorObservers: [
ContentsquareNavigatorObserver(),
TabRouteObserver(),
],
);

If you use a pageBuilder in your routes, make sure to set the page .name.

Example using GoRoute:

GoRoute(
path: 'checkout',
name: 'Checkout Screen',
pageBuilder: (context, state) => MaterialPage(
name: 'Checkout Screen',
key: ValueKey(state.pathParameters),
fullscreenDialog: true,
child: const CheckoutScreen(),
),
),