fix(analytics): resolve sessionId before assigning trackScreen state (v1.12.1+17)

The sessionId getter mints a fresh session on cold start or after a
30-min lapse, and as a side effect nulls _currentScreenName/_currentStepUid.
trackScreen() set those fields before calling the sessionId getter, so the
mint-on-cold-start side effect could immediately null the fields it had
just set, breaking the same-screen idempotency check and firing a
duplicate session_enter right after cold start.

Fix: resolve visitorId and sessionId into local variables before
assigning _currentScreenName/_currentStepUid, and use the locals in the
_enqueue() payload.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 14:58:56 +10:00
co-authored by Claude Sonnet 5
parent 729822c97e
commit 7c9e730cf0
2 changed files with 21 additions and 4 deletions
+20 -3
View File
@@ -57,7 +57,13 @@
/// `flutter_screen_views` row per screen; this is a Phase-1 backend gap,
/// not a client bug, and applies identically here).
///
/// Version: 1.0.0 | Created: 2026-09-25
/// Version: 1.0.1 | Created: 2026-09-25 | Updated: 2026-09-25
/// v1.0.1: trackScreen() resolved visitorId/sessionId into locals
/// (resolvedVisitorId/resolvedSessionId) BEFORE assigning
/// _currentScreenName/_currentStepUid, so the sessionId getter's
/// mint-on-cold-start side effect (which nulls those fields) can no
/// longer race the same-screen idempotency check and cause a duplicate
/// session_enter right after cold start.
library;
import 'dart:async';
@@ -365,6 +371,17 @@ class SessionJourney extends WidgetsBindingObserver {
return;
}
_leaveCurrentScreen();
// Resolve visitorId/sessionId BEFORE assigning _currentScreenName:
// sessionId's getter mints (and persists) a fresh session on a lapsed
// or cold window, and as a documented side effect of that mint it
// nulls _currentScreenName/_currentStepUid so the next trackScreen()
// re-enters cleanly under the new session. Reading it after already
// setting _currentScreenName below would immediately undo that
// assignment on every cold start, breaking this method's own
// same-screen idempotency and causing a duplicate session_enter on
// the very next call for the same screen. Found and fixed 2026-09-25.
final resolvedVisitorId = visitorId;
final resolvedSessionId = sessionId;
final stepUid = newUuidV4();
_currentScreenName = screenName;
_currentStepUid = stepUid;
@@ -373,8 +390,8 @@ class SessionJourney extends WidgetsBindingObserver {
_enqueue({
'action': 'session_enter',
'appKey': sessionJourneyAppKey,
'visitorId': visitorId,
'sessionId': sessionId,
'visitorId': resolvedVisitorId,
'sessionId': resolvedSessionId,
'stepUid': stepUid,
'platform': kIsWeb ? 'web' : 'unknown',
'appVersion': appVersion,