Mobile Development 9 min read

High‑Accuracy User Behavior Tracking in Flutter for Xianyu

To replace the native‑only tracking used by Xianyu after its migration to Flutter, the team built a high‑accuracy solution that mirrors the Flutter navigation stack with an index list, correctly fires enter/leave events on pushes, pops and filtered dialogs, and adds exposure detection based on 50 % visibility for 500 ms, ultimately delivering 100 % tracking accuracy in production.

Xianyu Technology
Xianyu Technology
Xianyu Technology
High‑Accuracy User Behavior Tracking in Flutter for Xianyu

Background: User‑behavior tracking (埋点) records a series of actions that are critical for business decisions. When Xianyu migrated its business code from Native to Flutter, the existing Native tracking scheme could not be applied, prompting the need for a high‑accuracy solution on Flutter.

Tracking point definition: On a typical user timeline, a user enters page A, sees button X, clicks it, and a new page B opens. Five events are recorded: entering A, exposing X, clicking X, leaving A, and entering B. The timing of these events is essential.

Implementation challenges: In Native, page enter/leave is detected via Activity.onResume/onPause (Android) or viewWillAppear/viewDidDisappear (iOS). Flutter uses FlutterActivity/FlutterViewController, which maintain their own page stack, making the native approach inapplicable. Registering a NavigatorObserver to listen for push/pop events has two problems: (1) when returning to a previous page, its push event is missed; (2) dialogs or bottom sheets also trigger push events even though the underlying page has not left.

Proposed solution: Maintain an index list that mirrors the Flutter page stack. On a push event, insert the new page’s index; if a previous page exists, first fire its leave event, then fire the new page’s enter event. On a pop event, fire the leaving page’s event, then check the top of the stack (using ModalRoute.of(context).isCurrent ) to decide whether to fire the previous page’s enter event. For dialogs/bottom sheets, use Route.overlayEntries and the opaque flag to filter out non‑full‑screen routes.

Key code snippets:

double _scrollContainerOffset = scrollNotification.metrics.pixels;
final RenderObject childRenderObject = context.findRenderObject();
final RenderAbstractViewport viewport = RenderAbstractViewport.of(childRenderObject);
if (viewport == null) return;
if (!childRenderObject.attached) return;
final RevealedOffset offsetToRevealTop = viewport.getOffsetToReveal(childRenderObject, 0.0);

Exposure detection: A slot is considered exposed when (1) at least 50% of its area is visible on screen, and (2) it stays visible for more than 500 ms. The algorithm tracks visibility state changes and timestamps, then triggers the exposure event when both conditions are satisfied.

if (isInvisible && exposureRatio >= 0.5) {
  // record visible, start timer
} else if (isVisible && exposureRatio < 0.5) {
  // record invisible
}
if (currentTime - startTime > 500ms) {
  // call exposure tracking API
}

Result: After multiple iterations, the Flutter tracking solution achieved 100% accuracy in production, providing reliable data for business analysis while remaining non‑intrusive and easy for developers to use.

Fluttermobile developmentanalyticsevent logginguser tracking
Xianyu Technology
Written by

Xianyu Technology

Official account of the Xianyu technology team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.