Customize autocaptured screens
Track screens automatically
Section titled Track screens automaticallyAutomatic screen view tracking can be done using the CSQNavigatorObserver. 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
import 'package:contentsquare/csq.dart';
MaterialApp( navigatorObservers: [ CSQNavigatorObserver(), ],);import 'package:contentsquare/csq.dart';
Navigator( observers: [ CSQNavigatorObserver(), ],);import 'package:contentsquare/csq.dart';
final _goRouter = GoRouter( observers: [ CSQNavigatorObserver(), ], routes: [...],);
MaterialApp.router( routerConfig: _goRouter,);The CSQNavigatorObserver can be configured with the following arguments:
ExcludeRouteFromTracking
Section titled ExcludeRouteFromTrackingAn optional callback function that determines whether a specific route should be excluded from tracking. The function takes a Route as input and returns a bool.
Use this function to selectively exclude certain routes from automatic tracking by returning true for those routes.
import 'package:contentsquare/csq.dart';
CSQNavigatorObserver( excludeRouteFromTracking: (route) { // Exclude a specific route name if (route.settings.name == '/myRoute') return true;
// Exclude popup routes (menus, popups, dialogs) if (route is PopupRoute) return true;
// Exclude dialog routes (AlertDialog, showDialog) // Many dialogs inherit from PopupRoute, but this is extra safety: if (route.runtimeType.toString().contains('DialogRoute')) return true;
// Exclude full-screen dialogs if (route is PageRoute && route.fullscreenDialog == true) return true;
return false; },)By default, all routes are tracked.
ScreenNameProvider
Section titled ScreenNameProviderAn 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.
import 'package:contentsquare/csq.dart';
CSQNavigatorObserver( screenNameProvider: (route) { final screenName = _formatScreenName(route.settings.name); return screenName; },)Troubleshooting
Section titled TroubleshootingAutoRoute package support
Section titled AutoRoute package supportIf you’re using the AutoRoute package ↗, refer to the CSQNavigatorAutoRouteObserver documentation for automatic screen tracking support, including tab navigation.
Pageview widget
Section titled Pageview widgetPageView widgets sometimes are used to build swipeable screens such as onboarding flows, multi-step forms, or carousels. However, because PageView navigation does not push new routes to the navigation stack, navigator observers are not triggered when pages change.
To ensure correct tracking, each page transition must be tracked manually using the onPageChanged callback.
For example, when using PageView.builder, you can track each page as an individual screen:
import 'package:contentsquare/csq.dart';
class PageViewTrackingExample extends StatelessWidget { const PageViewTrackingExample({super.key});
final _controller = PageController();
// Define meaningful screen names for each page final _pageNames = const <String>[ 'onboarding_step_1', 'onboarding_step_2', 'onboarding_step_3', ];
@override Widget build(BuildContext context) { return PageView.builder( controller: _controller, itemCount: _pageNames.length, onPageChanged: (index) { // Track screen view manually for each page change CSQ().trackScreenview(screenName: _pageNames[index]); }, itemBuilder: (_, index) { return OnboardingPage(step: index); }, ); }}PageBuilder
Section titled PageBuilderIf you use a pageBuilder in your routes, make sure to set the page .name.
Example using GoRoute:
import 'package:contentsquare/csq.dart';
GoRoute( path: 'checkout', name: 'Checkout Screen', pageBuilder: (context, state) => MaterialPage(
name: 'Checkout Screen', key: ValueKey(state.pathParameters), fullscreenDialog: true, child: const CheckoutScreen(), ),),TabBar
Section titled TabBarTabBar 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 track tab changes correctly, you need to manually call trackScreenview when the active tab changes. Here’s an example using TabController:
import 'package:contentsquare/csq.dart';
class TabBarExample extends StatefulWidget { const TabBarExample({super.key});
@override State<TabBarExample> createState() => _TabBarExampleState();}
class _TabBarExampleState extends State<TabBarExample> with SingleTickerProviderStateMixin { late TabController _tabController;
// Define meaningful screen names for each tab final _tabNames = const <String>[ 'home_tab', 'search_tab', 'profile_tab', ];
@override void initState() { super.initState(); _tabController = TabController(length: _tabNames.length, vsync: this);
// Track initial tab CSQ().trackScreenview(screenName: _tabNames[_tabController.index]);
// Listen to tab changes _tabController.addListener(() { if (!_tabController.indexIsChanging) { CSQ().trackScreenview(screenName: _tabNames[_tabController.index]); } }); }
@override void dispose() { _tabController.dispose(); super.dispose(); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( bottom: TabBar( controller: _tabController, tabs: const [ Tab(icon: Icon(Icons.home), text: 'Home'), Tab(icon: Icon(Icons.search), text: 'Search'), Tab(icon: Icon(Icons.person), text: 'Profile'), ], ), ), body: TabBarView( controller: _tabController, children: const [ HomeTab(), SearchTab(), ProfileTab(), ], ), ); }}