Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Track and Analyze User Navigation Patterns in FlutterFlow

Track user navigation patterns in FlutterFlow by logging screen_view events with session context to a Firestore `navigationEvents` collection — not just Firebase Analytics. Store the previous screen on each event to build a transition matrix. Use a Cloud Function to aggregate transition counts and identify drop-off points. Firebase Analytics alone gives 14-month retention with limited path analysis.

What you'll learn

  • Log screen_view events with session ID and previous screen to Firestore on every page load
  • Build a transition matrix Cloud Function that counts screen-to-screen navigation pairs
  • Identify drop-off points by comparing entry counts vs. exit counts per screen
  • Create a flow visualization component in FlutterFlow that displays path data
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner11 min read40-55 minFlutterFlow Free+March 2026RapidDev Engineering Team
TL;DR

Track user navigation patterns in FlutterFlow by logging screen_view events with session context to a Firestore `navigationEvents` collection — not just Firebase Analytics. Store the previous screen on each event to build a transition matrix. Use a Cloud Function to aggregate transition counts and identify drop-off points. Firebase Analytics alone gives 14-month retention with limited path analysis.

Why Firebase Analytics Alone Is Not Enough for Path Analysis

Firebase Analytics tracks screen views automatically in FlutterFlow apps, but its path analysis capabilities are limited. The Explorer report shows top paths but not full Sankey-style flow diagrams. Data is retained for only 14 months. You cannot run custom SQL against it without BigQuery linking (which requires a paid plan). Most importantly, you cannot join navigation events with your Firestore user data to answer questions like 'what path do paying users take that free users do not?' The solution is to log navigation events to your own Firestore collection alongside Firebase Analytics. This gives you complete ownership of the data, unlimited retention, and the ability to cross-join with any other Firestore data using Cloud Functions.

Prerequisites

  • FlutterFlow project with Firebase Firestore and Firebase Authentication enabled
  • Firebase Analytics connected to your FlutterFlow project
  • Basic understanding of FlutterFlow page On Load actions
  • An admin dashboard page or external analytics tool for viewing results

Step-by-step guide

1

Create the `navigationEvents` Firestore collection schema

In FlutterFlow, create a Firestore collection named `navigationEvents`. Add these fields: `userId` (String — Firebase Auth UID, or 'anonymous'), `sessionId` (String — a UUID generated at app launch and stored in App State), `screenName` (String — the current page name), `previousScreen` (String, nullable — the screen navigated from), `timestamp` (Timestamp), `platform` (String — 'ios', 'android', or 'web'), `appVersion` (String). The `sessionId` is the key field that ties events together into a session. Generate it once on app startup using a Custom Function that calls Dart's `Uuid` package and store it in an App State variable. This lets you reconstruct the exact path a user took in a single session.

Expected result: The `navigationEvents` collection appears in the FlutterFlow Firestore schema with all fields.

2

Generate a session ID on app launch and store it in App State

Go to FlutterFlow > App State and add a variable named `sessionId` (String, persisted: false — resets on each app launch). Add a Custom Function named `generateUuid` that returns a new UUID string: in Dart this is `const Uuid().v4()` from the `uuid` package (add `uuid: ^4.0.0` to your pubspec.yaml). On your app's initial page (usually the splash or entry page), add an On Page Load action: call the `generateUuid` Custom Function and set the `sessionId` App State variable to the result. Also set a `previousScreen` App State variable (String, default empty string) that you will update on every navigation.

custom_functions/generate_uuid.dart
1// Custom Function: generateUuid
2import 'package:uuid/uuid.dart';
3
4String generateUuid() {
5 return const Uuid().v4();
6}

Expected result: A unique session ID is generated each time the app launches and stored in App State.

3

Log a navigation event on every page's On Load action

On each page in FlutterFlow, go to the page settings and add an On Page Load action chain. The chain should: (1) Run a Firestore Create Document action on `navigationEvents` with all fields populated — `userId` from `currentUser.uid` (or 'anonymous' if not signed in), `sessionId` from App State, `screenName` as a hardcoded string matching the page name, `previousScreen` from the `previousScreen` App State variable, `timestamp` as the current time, `platform` from the `Platform` Custom Function. (2) After logging, update the `previousScreen` App State variable to the current page name. This two-step pattern — log then update — ensures each event correctly captures where the user came from.

Expected result: Navigating through the app creates documents in `navigationEvents` with the correct `previousScreen` values forming a chain.

4

Build a transition matrix Cloud Function

Create a Firebase Cloud Function named `buildTransitionMatrix`. It queries `navigationEvents` for the last 30 days (configurable), groups events by `previousScreen -> screenName` pairs, and counts occurrences of each pair. The result is a matrix where each row represents a source screen, each column a destination screen, and the value is the number of times users navigated from source to destination. The function saves the result to a Firestore document `analyticsCache/transitionMatrix` with a `computedAt` timestamp. Schedule it to run daily via Cloud Scheduler. In FlutterFlow, read the `analyticsCache/transitionMatrix` document to display the most common navigation paths on an admin dashboard.

functions/analytics.js
1const { onCall } = require('firebase-functions/v2/https');
2const { getFirestore, Timestamp } = require('firebase-admin/firestore');
3
4exports.buildTransitionMatrix = onCall(async (request) => {
5 const db = getFirestore();
6 const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
7
8 const snapshot = await db
9 .collection('navigationEvents')
10 .where('timestamp', '>=', Timestamp.fromDate(thirtyDaysAgo))
11 .where('previousScreen', '!=', '')
12 .get();
13
14 const matrix = {};
15 const screenCounts = {};
16
17 snapshot.forEach((doc) => {
18 const { previousScreen, screenName } = doc.data();
19 const key = `${previousScreen}___${screenName}`;
20 matrix[key] = (matrix[key] || 0) + 1;
21 screenCounts[screenName] = (screenCounts[screenName] || 0) + 1;
22 });
23
24 // Convert to sorted array of transitions
25 const transitions = Object.entries(matrix)
26 .map(([key, count]) => {
27 const [from, to] = key.split('___');
28 return { from, to, count };
29 })
30 .sort((a, b) => b.count - a.count)
31 .slice(0, 50); // Top 50 transitions
32
33 await db.collection('analyticsCache').doc('transitionMatrix').set({
34 transitions,
35 screenCounts,
36 computedAt: new Date(),
37 periodDays: 30,
38 });
39
40 return { transitionCount: transitions.length };
41});

Expected result: The `analyticsCache/transitionMatrix` document contains a sorted list of navigation transitions with counts.

5

Identify drop-off screens by comparing entry vs exit counts

A drop-off screen is one that users navigate to but rarely navigate away from — they abandon the app. Extend the Cloud Function to compute, for each screen: `entryCount` (how many times it appeared as `screenName`) and `exitCount` (how many times it appeared as `previousScreen`). The drop-off rate is `1 - (exitCount / entryCount)`. A rate above 60% on a non-terminal screen (like a checkout page that should lead to success) indicates a problem. Store these per-screen metrics in the `analyticsCache/dropoffAnalysis` document. In FlutterFlow, display screens sorted by drop-off rate descending on your admin dashboard so the worst drop-off points are at the top.

Expected result: The admin dashboard shows a ranked list of screens by drop-off rate. Screens with unexpectedly high drop-offs are easy to identify.

6

Build a flow visualization component for the admin dashboard

In FlutterFlow, create a Component named `NavigationFlowChart`. It reads the `analyticsCache/transitionMatrix` document and displays the top 10 transitions as a vertical list of cards. Each card shows: an arrow from `from` screen name to `to` screen name, the count as a number, and a horizontal progress bar showing the count as a proportion of the maximum count. Sort by count descending. Add a Text widget at the top showing the data freshness (`computedAt` formatted as 'Updated X minutes ago'). Add a Refresh button that calls the `buildTransitionMatrix` Cloud Function. This is simpler to build in FlutterFlow than a true Sankey diagram but provides the same insight into the most common navigation paths.

Expected result: The admin dashboard shows a ranked list of the top 10 navigation transitions with counts and relative proportions.

Complete working example

functions/analytics.js
1const { onCall } = require('firebase-functions/v2/https');
2const { initializeApp } = require('firebase-admin/app');
3const { getFirestore, Timestamp } = require('firebase-admin/firestore');
4
5initializeApp();
6
7exports.buildNavigationAnalytics = onCall(async (request) => {
8 const uid = request.auth?.uid;
9 if (!uid) throw new Error('Unauthenticated');
10 // Optional: check if user is admin
11
12 const periodDays = request.data?.periodDays || 30;
13 const db = getFirestore();
14 const cutoff = new Date(Date.now() - periodDays * 24 * 60 * 60 * 1000);
15
16 const snapshot = await db
17 .collection('navigationEvents')
18 .where('timestamp', '>=', Timestamp.fromDate(cutoff))
19 .get();
20
21 const transitionCounts = {};
22 const screenEntry = {};
23 const screenExit = {};
24 const sessionPaths = {};
25
26 snapshot.forEach((doc) => {
27 const { previousScreen, screenName, sessionId } = doc.data();
28
29 // Count screen entries
30 screenEntry[screenName] = (screenEntry[screenName] || 0) + 1;
31
32 // Count transitions
33 if (previousScreen) {
34 screenExit[previousScreen] = (screenExit[previousScreen] || 0) + 1;
35 const key = `${previousScreen}|||${screenName}`;
36 transitionCounts[key] = (transitionCounts[key] || 0) + 1;
37 }
38
39 // Track session paths
40 if (!sessionPaths[sessionId]) sessionPaths[sessionId] = [];
41 sessionPaths[sessionId].push(screenName);
42 });
43
44 // Build top transitions
45 const transitions = Object.entries(transitionCounts)
46 .map(([key, count]) => {
47 const [from, to] = key.split('|||');
48 return { from, to, count };
49 })
50 .sort((a, b) => b.count - a.count)
51 .slice(0, 50);
52
53 // Build drop-off analysis
54 const dropoffAnalysis = Object.keys(screenEntry).map((screen) => {
55 const entries = screenEntry[screen] || 0;
56 const exits = screenExit[screen] || 0;
57 const dropoffRate = entries > 0 ? Math.round((1 - exits / entries) * 100) : 0;
58 return { screen, entries, exits, dropoffRate };
59 }).sort((a, b) => b.dropoffRate - a.dropoffRate);
60
61 // Find most common full session paths (top 5)
62 const pathCounts = {};
63 Object.values(sessionPaths).forEach((path) => {
64 const key = path.slice(0, 5).join(' > ');
65 pathCounts[key] = (pathCounts[key] || 0) + 1;
66 });
67 const topPaths = Object.entries(pathCounts)
68 .sort((a, b) => b[1] - a[1])
69 .slice(0, 10)
70 .map(([path, count]) => ({ path, count }));
71
72 const result = {
73 transitions,
74 dropoffAnalysis,
75 topPaths,
76 totalEvents: snapshot.size,
77 totalSessions: Object.keys(sessionPaths).length,
78 computedAt: new Date(),
79 periodDays,
80 };
81
82 await db.collection('analyticsCache').doc('navigationAnalytics').set(result);
83 return { success: true, totalEvents: snapshot.size };
84});

Common mistakes

Why it's a problem: Logging screen views only in Firebase Analytics and relying on it for path analysis

How to avoid: Log navigation events to your own Firestore collection in parallel with Firebase Analytics. This gives you full SQL-equivalent access via Cloud Functions, unlimited retention, and the ability to join with any other app data.

Why it's a problem: Forgetting to update the `previousScreen` App State variable after logging the event

How to avoid: In the On Page Load action chain, log first, then update `previousScreen`. Use a reusable Custom Action to enforce this order consistently across all pages.

Why it's a problem: Running the transition matrix aggregation query client-side in FlutterFlow

How to avoid: Always aggregate navigation data in a Cloud Function. Cache the result in a single `analyticsCache` Firestore document and read that document in FlutterFlow — it costs one read regardless of how many events exist.

Best practices

  • Log navigation events to Firestore alongside Firebase Analytics — own your data with unlimited retention and full query access.
  • Use a session ID stored in App State to tie navigation events into complete user journeys across a single app session.
  • Cache aggregated analytics results in a dedicated Firestore `analyticsCache` collection — never aggregate raw events client-side.
  • Include a `previousScreen` field in every navigation event to enable transition matrix analysis without post-processing joins.
  • Add optional `properties` JSON to navigation events so page-specific business context can enrich path analysis.
  • Schedule the aggregation Cloud Function to run daily and store a `computedAt` timestamp so the dashboard shows data freshness.
  • Exclude internal admin pages and error pages from drop-off analysis by filtering them out in the Cloud Function.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Firestore collection `navigationEvents` with fields: userId, sessionId, screenName, previousScreen, timestamp. I want to write a Cloud Function that computes: (1) a transition matrix showing count of each from/to screen pair, (2) a drop-off rate per screen (entries minus exits divided by entries), and (3) the top 10 most common complete session paths. Return all three as a single JSON object and save it to Firestore. Write the complete Cloud Function.

FlutterFlow Prompt

In FlutterFlow, I want to add a navigation event logger to every page in my app. The logger should read a sessionId from App State, write a document to Firestore with screenName and previousScreen, then update the previousScreen App State variable. I have 20 pages. What is the most efficient way to add this to all pages — should I use a Custom Action, a reusable Component, or something else? How do I add it without manually editing each page's On Load action?

Frequently asked questions

Will logging a Firestore event on every page navigation slow down my app?

No. Firestore writes are asynchronous — the write happens in the background without blocking the UI. The On Page Load action starts the Firestore write and immediately continues rendering the page. You will not notice any performance impact for typical usage volumes.

How much will Firestore cost to store all these navigation events?

Firestore charges $0.18 per 100,000 writes. An active user navigating through 20 screens per session costs about $0.000036 per session. At 1,000 daily active users, that is $36 per million sessions — roughly $1-2/month for most early-stage apps. Set up a lifecycle policy to delete events older than 90 days to keep storage costs low.

How do I track anonymous users who are not signed in?

Use Firebase Anonymous Authentication. In FlutterFlow, enable Anonymous sign-in and sign users in anonymously on app launch if they are not already authenticated. Anonymous users get a stable UID for the duration of the installation. Store their UID in navigation events. When they sign up later, Firebase links the anonymous account to the new account, preserving the UID.

Can I see individual user paths, not just aggregate statistics?

Yes. Query the `navigationEvents` collection filtered by a specific `userId` and ordered by `timestamp`, then group by `sessionId`. This gives you the exact sequence of screens for every session that user has ever had. This is useful for debugging a specific user's reported issue or understanding a high-value customer's behavior.

How do I set up automatic deletion of old navigation events?

Create a Cloud Scheduler job that runs weekly and calls a Cloud Function that deletes `navigationEvents` documents where `timestamp` is older than 90 days. Use a batched delete to avoid timeout issues on large collections. Alternatively, use Firebase's built-in TTL feature (Firestore TTL) by setting an `expireAt` field on each document — Firestore automatically deletes documents after that time at no additional cost.

Does this work alongside Firebase Analytics or does it replace it?

It works alongside Firebase Analytics. Keep Firebase Analytics enabled — it provides free crash reporting, audience segmentation, and integration with Google Ads. The Firestore-based navigation logging supplements it with custom path analysis and data ownership. You get the best of both: Firebase Analytics for standard marketing and growth metrics, and your Firestore events for deep product analytics.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.