Skip to content

feat(flutter): record screen views (Navigate) from Dart#627

Open
abelonogov-ld wants to merge 1 commit into
mainfrom
andrey/flutter-sr-screenview
Open

feat(flutter): record screen views (Navigate) from Dart#627
abelonogov-ld wants to merge 1 commit into
mainfrom
andrey/flutter-sr-screenview

Conversation

@abelonogov-ld

Copy link
Copy Markdown
Contributor

Summary

Phase 1 of adding Flutter-sourced Session Replay events (see plan). Flutter renders into a single native Activity/UIViewController, so the native SDK's automatic screen detection never sees Flutter route changes. This adds a trackScreenView path so navigation appears on the Session Replay timeline as a Navigate event (and as a screen_view span).

  • Bridge: new Pigeon LDNativeApi.trackScreenView(name, screenClass, screenId, category, properties), wired on iOS/Android to the existing public LDObserve.trackScreenView (which funnels into the screen_view span + Session Replay Navigate).
  • Public API: LDObserve.trackScreenView(name, {screenClass, screenId, category, properties}), with a platform-split ScreenViewRecorder (native bridge on mobile; a screen_view span gated by analytics.pageViews on web/stub).
  • Auto-capture: LDNavigatorObserver (a NavigatorObserver) emits screen views on didPush/didPop/didReplace using route.settings.name, with a customizable screenNameExtractor and optional category.
  • Conventions/config: ScreenViewConvention (span name screen_view, event.name/event.screen_class/event.screen_id/event.category); new pageViewsEnabled config mapped from analytics.pageViews.
  • Docs + unit tests. All additive (no deletions); Pigeon regen only adds the new method.

Why

AnalyticsOptions.pageViews is native-only (Android lifecycle / iOS no-op) and cannot observe Flutter's Navigator, so screen views had no way to reach Session Replay from a Flutter app.

Test plan

  • dart analyze clean, dart format clean
  • flutter test (153 tests) pass, incl. new screen_view_convention_test.dart
  • On-device: LDObserve.trackScreenView('Home') and LDNavigatorObserver produce Navigate events in a Session Replay recording (iOS + Android)
  • Web: screen_view span emitted when analytics.pageViews is enabled, suppressed when disabled

Follow-up

Phase 2 (proper RRWeb Click with Flutter widget info) is tracked separately and requires native additions across all three repos.

Made with Cursor

Flutter renders into a single native Activity/UIViewController, so the native
SDK's automatic screen detection never sees Flutter route changes. Add a
trackScreenView path so navigation shows up on the Session Replay timeline as a
Navigate event (and as a screen_view span):

- Pigeon LDNativeApi.trackScreenView bridged to LDObserve.trackScreenView on
  iOS and Android.
- Public LDObserve.trackScreenView(name, {screenClass, screenId, category,
  properties}) with a platform-split recorder (native bridge on mobile,
  screen_view span gated by analytics.pageViews on web/stub).
- LDNavigatorObserver to auto-emit screen views on route push/pop/replace,
  with a customizable screen-name extractor.
- ScreenViewConvention + tests; README docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
@abelonogov-ld abelonogov-ld requested a review from a team as a code owner June 13, 2026 01:19

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ca47068. Configure here.

super.didPop(route, previousRoute);
// After a pop the previously-underlying route becomes visible again, so it
// is the screen the user navigates back to.
_record(previousRoute);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pop emits duplicate screen views

Medium Severity

LDNavigatorObserver always calls trackScreenView for previousRoute on every didPop, even when the popped route was skipped on didPush because it had no screen name. Dismissing unnamed overlays (typical dialogs and bottom sheets) therefore emits an extra Navigate / screen_view for the route already underneath, duplicating the prior screen view in Session Replay and analytics.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ca47068. Configure here.

// After a pop the previously-underlying route becomes visible again, so it
// is the screen the user navigates back to.
_record(previousRoute);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Route removal skips screen tracking

Low Severity

Auto-capture listens only to didPush, didReplace, and didPop. When the top route is removed with Navigator.removeRoute (or similar APIs that notify didRemove instead of didPop), the newly visible route never triggers trackScreenView, so Session Replay and spans miss that navigation.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ca47068. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants