Skip to main content

Automatic Screen Tracking

The Conviso Flutter SDK provides automatic screen tracking through a NavigatorObserver that monitors route changes in your app.

Overview​

When enabled, the screen tracker will automatically:

  • ✅ Track all screen views in your app
  • ✅ Store anonymous screen views until a user is identified
  • ✅ Associate anonymous screen views with users once they are identified
  • ✅ Include screen name, route, and timestamp in the tracking data

Setup​

Step 1: Enable in Configuration​

Set autoTrackScreens: true in your SDK configuration:

await OpenCDPSDK.initialize(
config: OpenCDPConfig(
cdpApiKey: 'your-api-key',
autoTrackScreens: true, // Enable automatic screen tracking
// ... other config options
),
);

Step 2: Add Navigator Observer​

Add the screen tracker to your app's navigator observers in your MaterialApp:

MaterialApp(
navigatorObservers: [
OpenCDPSDK.instance.screenTracker!,
],
// ... other app configuration
home: MyHomePage(),
);
Required Setup

Both steps are required for automatic screen tracking to work. The configuration enables the feature, and the navigator observer performs the actual tracking.

Complete Example​

Here's a complete example showing screen tracking setup:

import 'package:flutter/material.dart';
import 'package:open_cdp_flutter_sdk/open_cdp_flutter_sdk.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

// Initialize SDK with screen tracking enabled
await OpenCDPSDK.initialize(
config: OpenCDPConfig(
cdpApiKey: 'your-api-key',
autoTrackScreens: true,
debug: true,
),
);

runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
// Add the screen tracker observer
navigatorObservers: [
OpenCDPSDK.instance.screenTracker!,
],
home: HomePage(),
routes: {
'/profile': (context) => ProfilePage(),
'/settings': (context) => SettingsPage(),
},
);
}
}

How It Works​

Anonymous Screen Views​

Before a user is identified, screen views are stored locally:

// User navigates to a screen (not yet identified)
Navigator.pushNamed(context, '/products');

// Screen view is stored locally as anonymous

Identified Screen Views​

Once a user is identified, all screen views (past and future) are associated with that user:

// Identify the user
await OpenCDPSDK.instance.identify(
identifier: 'user123',
properties: {'email': '[email protected]'},
);

// All previous anonymous screen views are now associated with user123
// Future screen views are automatically tracked with user123

Manual Screen Tracking​

You can also manually track screen views if needed:

await OpenCDPSDK.instance.trackScreenView(
title: 'Product Details',
properties: {
'product_id': '123',
'category': 'Electronics',
'source': 'search',
},
);

Tracked Data​

Each screen view event includes:

  • Screen Name: The route name or title
  • Route: The full route path
  • Timestamp: When the screen was viewed
  • Custom Properties: Any additional properties you provide

Best Practices​

1. Use Named Routes​

Named routes provide better screen names in your tracking data. Auto-track uses RouteSettings.name only — it never falls back to route.toString(), so unnamed routes are skipped (they will not appear in the in-app Include / Exclude catalog).

// ✅ Good - Named route / GoRouter path
Navigator.pushNamed(context, '/product-details');
// or GoRouter: context.go('/product-details');

// ✅ Good - Explicit name on a custom route
Navigator.push(
context,
MaterialPageRoute(
settings: const RouteSettings(name: '/product-details'),
builder: (context) => ProductDetailsPage(),
),
);

// ❌ Skipped by auto-track — no RouteSettings.name
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProductDetailsPage()),
);

Name dialogs and sheets​

Modals and bottom sheets need an explicit name if you want them in page rules:

showModalBottomSheet(
context: context,
routeSettings: const RouteSettings(name: '/sheet/filters'),
builder: (_) => FiltersSheet(),
);

showDialog(
context: context,
routeSettings: const RouteSettings(name: '/dialog/confirm'),
builder: (_) => ConfirmDialog(),
);

Recommended shapes: path-like (/profile), short labels (Home), or segments (checkout/payment). Avoid framework class dumps.

2. Add Custom Properties​

Enhance screen view tracking with custom properties:

await OpenCDPSDK.instance.trackScreenView(
title: 'Product Details',
properties: {
'product_id': product.id,
'category': product.category,
'price': product.price,
'source': 'search', // How the user got here
},
);

3. Identify Users Early​

Identify users as soon as possible to minimize anonymous screen views:

// Identify immediately after login
await handleLogin(email, password);
await OpenCDPSDK.instance.identify(
identifier: userId,
properties: {'email': email},
);

Disabling Automatic Tracking​

If you want to manually control screen tracking, simply:

  1. Set autoTrackScreens: false in your configuration
  2. Don't add the navigator observer
  3. Use trackScreenView() manually when needed
OpenCDPConfig(
cdpApiKey: 'your-api-key',
autoTrackScreens: false, // Disable automatic tracking
// ... other config
)

In-app page rules catalog​

Each screen_view (automatic or trackScreenView) also feeds the workspace screen catalog used by Include / Exclude screens in the Conviso in-app editor. The backend stores unique properties.screen values per workspace; it does not treat screen_view as a normal analytics event for BigQuery or campaign triggers.

  • Use stable screen titles so dashboard picks match setCurrentScreen / sync ?screen=.
  • Until traffic reports views, the editor list may be empty — marketers can still type names manually.
  • Unnamed Flutter routes and technical toString() dumps are not catalogued. See Automatic Screen Tracking.
  • In the editor, All screens + Exclude means “everywhere except…”. Empty include is the persisted form of All.

Troubleshooting​

Screen Views Not Tracking​

If screen views aren't being tracked:

  1. ✅ Verify autoTrackScreens: true in configuration
  2. ✅ Verify you added the navigator observer to MaterialApp
  3. ✅ Check debug logs for errors
  4. ✅ Ensure the SDK is initialized before runApp()

If you get a null error accessing screenTracker:

// ❌ Wrong - screenTracker is null when autoTrackScreens is false
navigatorObservers: [
OpenCDPSDK.instance.screenTracker!, // Will throw if autoTrackScreens is false
]

// ✅ Correct - Check if it exists first
navigatorObservers: [
if (OpenCDPSDK.instance.screenTracker != null)
OpenCDPSDK.instance.screenTracker!,
]