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

How to Build a Personalized Shopping Experience in FlutterFlow

Create a personalized shopping experience by tracking user behavior in a Firestore user_behavior document that records viewed products, purchase history, and search terms. The homepage dynamically renders sections like Recently Viewed, Because You Bought X, and Trending in Your Category using Backend Queries filtered by the user's behavior data. A Cloud Function generates recommendations and sends abandoned cart reminder emails. New users with no behavior data see trending and popular items as a fallback.

What you'll learn

  • How to track user behavior events like views, purchases, and searches in Firestore
  • How to build a personalized homepage with dynamic sections based on user data
  • How to implement product recommendations using purchase category matching
  • How to set up abandoned cart email reminders via Cloud Functions
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read25-30 minFlutterFlow Pro+ (Cloud Functions required for recommendations and emails)March 2026RapidDev Engineering Team
TL;DR

Create a personalized shopping experience by tracking user behavior in a Firestore user_behavior document that records viewed products, purchase history, and search terms. The homepage dynamically renders sections like Recently Viewed, Because You Bought X, and Trending in Your Category using Backend Queries filtered by the user's behavior data. A Cloud Function generates recommendations and sends abandoned cart reminder emails. New users with no behavior data see trending and popular items as a fallback.

Building a Personalized Shopping Experience in FlutterFlow

Generic storefronts show every user the same content. Personalization transforms the shopping experience by surfacing products based on individual browsing and purchase history. This tutorial builds user behavior tracking, a dynamic personalized homepage with recently viewed items and category-based recommendations, a hero banner that adapts to user preferences, and an abandoned cart recovery system.

Prerequisites

  • A FlutterFlow project with an e-commerce product catalog already set up
  • Firebase project with Firestore and Cloud Functions enabled
  • A products collection with fields: name, category, price, imageUrl, and popularity score
  • Basic familiarity with Backend Queries and App State in FlutterFlow

Step-by-step guide

1

Create the user behavior tracking schema

Create a `user_behavior` collection where each document ID matches the user's UID. Add fields: recentViews (Array of Maps, each with productId, category, and viewedAt), purchaseCategories (Array of Strings — categories the user has bought from), searchTerms (Array of Strings — recent search queries), topCategory (String — most frequent category from views and purchases), and interactionCount (int — total tracked interactions). On every product detail page load, add a Custom Action that appends to the recentViews array using FieldValue.arrayUnion with the product details. Cap the array at 20 items by using a Cloud Function that trims older entries.

Expected result: User browsing behavior is tracked in Firestore automatically as they view products, search, and make purchases.

2

Build the Recently Viewed horizontal carousel

On the homepage, add a 'Recently Viewed' section header with a horizontal ListView. Read the current user's user_behavior document and extract the recentViews array. For each entry, query the products collection by productId to get the full product details. Display each product in a compact Container card within the horizontal list showing the product image, name, and price. If recentViews is empty (new user), hide the entire section using Conditional Visibility that checks recentViews length > 0. Order items by viewedAt descending so the most recently viewed product appears first.

Expected result: Returning users see their recently viewed products in a horizontal scrollable carousel. New users with no history do not see this section.

3

Implement category-based product recommendations

Create a Cloud Function named generateRecommendations triggered on a schedule (hourly) or on user_behavior document update. The function reads the user's purchaseCategories and topCategory fields. It queries the products collection for items in those categories that the user has NOT already viewed (exclude productIds from recentViews). Rank by popularity score descending and write the top 10 product IDs to a `users/{uid}/recommendations` document. In FlutterFlow, add a 'Recommended For You' section on the homepage with a horizontal ListView bound to a Backend Query that fetches products by IDs from the recommendations document. The section title can be personalized: 'Because You Bought [category]'.

generateRecommendations.js
1// Cloud Function: generateRecommendations
2exports.generateRecommendations = functions.firestore
3 .document('user_behavior/{userId}')
4 .onWrite(async (change, context) => {
5 const userId = context.params.userId;
6 const behavior = change.after.data();
7 if (!behavior) return;
8
9 const viewedIds = (behavior.recentViews || [])
10 .map(v => v.productId);
11 const categories = behavior.purchaseCategories || [];
12 const topCat = behavior.topCategory;
13
14 const targetCats = topCat
15 ? [topCat, ...categories] : categories;
16 const uniqueCats = [...new Set(targetCats)]
17 .slice(0, 3);
18
19 if (uniqueCats.length === 0) return;
20
21 const products = await db
22 .collection('products')
23 .where('category', 'in', uniqueCats)
24 .orderBy('popularity', 'desc')
25 .limit(20)
26 .get();
27
28 const recommended = products.docs
29 .filter(d => !viewedIds.includes(d.id))
30 .slice(0, 10)
31 .map(d => d.id);
32
33 await db.doc(`users/${userId}/recommendations/main`)
34 .set({
35 productIds: recommended,
36 category: uniqueCats[0],
37 updatedAt: admin.firestore.FieldValue
38 .serverTimestamp(),
39 });
40 });

Expected result: Users see product recommendations based on their purchase and browsing categories. Products they have already viewed are excluded.

4

Create a dynamic hero banner based on user preferences

Create a `banners` collection with documents for each promotional banner containing: imageUrl (String), title (String), targetCategory (String), linkProductId or linkCategory (String), and priority (int). On the homepage, query banners ordered by priority descending. Filter the results using a Custom Function that prioritizes banners matching the user's topCategory from user_behavior. If the user's top category is 'Electronics', show the Electronics sale banner first. For new users with no topCategory, show the default highest-priority banner. Display the banner as a full-width Container with the image, overlaid title text, and a tap action that navigates to the linked product or category page.

Expected result: The hero banner dynamically shows promotions relevant to the user's shopping interests, with a fallback to popular promotions for new users.

5

Add a Trending in Your Category section with fallback for new users

Add a third homepage section titled 'Trending in [category]' where [category] is the user's topCategory. Query products where category equals the user's topCategory, ordered by a trendingScore or recentSalesCount descending, limited to 10. Display in a horizontal ListView like the other sections. For new users (interactionCount < 5 or topCategory is null), show a 'Popular Right Now' fallback section instead. This queries products ordered by popularity descending across all categories. Use Conditional Visibility to switch between the personalized and fallback sections based on the interactionCount threshold.

Expected result: Established users see trending products in their preferred category. New users see globally popular products until enough behavior data is collected.

6

Set up abandoned cart email reminders via Cloud Function

Create a scheduled Cloud Function named checkAbandonedCarts that runs every 6 hours. The function queries users with non-empty cart App State (stored in a `carts` collection with userId, items array, and lastUpdatedAt). For carts not updated in 24 hours where no matching order exists, compose and send a reminder email using SendGrid or Firebase Extensions with a personalized message listing the cart items, their images, and a deep link back to the checkout page. Record the reminder in a `cart_reminders` collection to avoid sending duplicate emails. Limit to one reminder per abandoned cart.

checkAbandonedCarts.js
1// Cloud Function: checkAbandonedCarts
2exports.checkAbandonedCarts = functions.pubsub
3 .schedule('every 6 hours')
4 .onRun(async () => {
5 const cutoff = new Date();
6 cutoff.setHours(cutoff.getHours() - 24);
7
8 const carts = await db.collection('carts')
9 .where('lastUpdatedAt', '<',
10 admin.firestore.Timestamp.fromDate(cutoff))
11 .where('itemCount', '>', 0)
12 .get();
13
14 for (const cartDoc of carts.docs) {
15 const cart = cartDoc.data();
16 // Check if reminder already sent
17 const existing = await db
18 .collection('cart_reminders')
19 .where('cartId', '==', cartDoc.id)
20 .limit(1).get();
21 if (!existing.empty) continue;
22
23 // Send email via SendGrid
24 // ... email sending logic with cart items
25
26 await db.collection('cart_reminders').add({
27 cartId: cartDoc.id,
28 userId: cart.userId,
29 sentAt: admin.firestore.FieldValue
30 .serverTimestamp(),
31 });
32 }
33 });

Expected result: Users with abandoned carts for 24+ hours receive a single reminder email with their cart items and a link to complete the purchase.

Complete working example

Personalization Cloud Functions
1// Cloud Functions: Personalized Shopping System
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4admin.initializeApp();
5const db = admin.firestore();
6
7// Generate recommendations on behavior change
8exports.generateRecommendations = functions.firestore
9 .document('user_behavior/{userId}')
10 .onWrite(async (change, context) => {
11 const userId = context.params.userId;
12 const behavior = change.after.data();
13 if (!behavior) return;
14 const viewedIds = (behavior.recentViews || []).map(v => v.productId);
15 const categories = behavior.purchaseCategories || [];
16 const topCat = behavior.topCategory;
17 const targetCats = [...new Set(
18 [topCat, ...categories].filter(Boolean)
19 )].slice(0, 3);
20 if (targetCats.length === 0) return;
21 const snapshot = await db.collection('products')
22 .where('category', 'in', targetCats)
23 .orderBy('popularity', 'desc').limit(20).get();
24 const recommended = snapshot.docs
25 .filter(d => !viewedIds.includes(d.id))
26 .slice(0, 10).map(d => d.id);
27 await db.doc(`users/${userId}/recommendations/main`).set({
28 productIds: recommended,
29 category: targetCats[0],
30 updatedAt: admin.firestore.FieldValue.serverTimestamp(),
31 });
32 });
33
34// Track product view
35exports.trackProductView = functions.https
36 .onCall(async (data, context) => {
37 if (!context.auth) return;
38 const { productId, category } = data;
39 const userId = context.auth.uid;
40 const ref = db.doc(`user_behavior/${userId}`);
41 await ref.set({
42 recentViews: admin.firestore.FieldValue.arrayUnion({
43 productId, category, viewedAt: new Date().toISOString(),
44 }),
45 interactionCount: admin.firestore.FieldValue.increment(1),
46 }, { merge: true });
47 // Update topCategory from view frequency
48 const doc = await ref.get();
49 const views = doc.data()?.recentViews || [];
50 const catCounts = {};
51 views.forEach(v => { catCounts[v.category] = (catCounts[v.category] || 0) + 1; });
52 const topCategory = Object.entries(catCounts)
53 .sort((a, b) => b[1] - a[1])[0]?.[0];
54 if (topCategory) await ref.update({ topCategory });
55 });
56
57// Abandoned cart check (every 6 hours)
58exports.checkAbandonedCarts = functions.pubsub
59 .schedule('every 6 hours')
60 .onRun(async () => {
61 const cutoff = new Date();
62 cutoff.setHours(cutoff.getHours() - 24);
63 const carts = await db.collection('carts')
64 .where('lastUpdatedAt', '<',
65 admin.firestore.Timestamp.fromDate(cutoff))
66 .where('itemCount', '>', 0).get();
67 for (const doc of carts.docs) {
68 const sent = await db.collection('cart_reminders')
69 .where('cartId', '==', doc.id).limit(1).get();
70 if (!sent.empty) continue;
71 // Send email reminder via SendGrid here
72 await db.collection('cart_reminders').add({
73 cartId: doc.id, userId: doc.data().userId,
74 sentAt: admin.firestore.FieldValue.serverTimestamp(),
75 });
76 }
77 });

Common mistakes when building a Personalized Shopping Experience in FlutterFlow

Why it's a problem: Personalizing for new users with no behavior data

How to avoid: Show trending and popular items as a fallback when interactionCount is below 5. Gradually transition to personalized content as more behavior data is collected. Always hide empty sections with Conditional Visibility.

Why it's a problem: Tracking behavior data without capping array sizes

How to avoid: Cap recentViews at 20-30 items. Use a Cloud Function triggered on update to trim the array, keeping only the most recent entries. Similarly, limit searchTerms to the last 10 queries.

Why it's a problem: Generating recommendations synchronously on every page load

How to avoid: Pre-compute recommendations via a Cloud Function triggered by behavior changes or on a schedule. Store the recommendation product IDs in a separate document. The homepage simply reads the pre-computed list.

Best practices

  • Hide empty personalization sections with Conditional Visibility instead of showing 'No items yet' messages
  • Use a minimum interaction threshold (5+ interactions) before showing personalized content to avoid poor recommendations
  • Pre-compute recommendations in Cloud Functions rather than calculating them on every page load
  • Cap all behavior tracking arrays (recentViews, searchTerms) to prevent unbounded document growth
  • Show the user's top category in section headers like 'Trending in Electronics' for a personal touch
  • Limit abandoned cart reminders to one email per cart to avoid spamming users
  • Track recommendation clicks as a behavior signal to improve future recommendations

Still stuck?

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

ChatGPT Prompt

I'm building a personalized e-commerce app in FlutterFlow. I need to track user behavior (views, purchases, searches), generate product recommendations based on purchase categories, create a dynamic homepage with Recently Viewed and recommended sections, and set up abandoned cart email reminders. Show me the Firestore schema and Cloud Functions.

FlutterFlow Prompt

Create a personalized homepage with three horizontal product carousels: Recently Viewed (from user behavior), Recommended For You (based on purchase categories), and Trending in Your Category. Add a dynamic hero banner that changes based on user preferences. For new users with no data, show popular products instead.

Frequently asked questions

How many interactions should I wait before personalizing?

Start showing personalized content after 5 product views or 1 purchase. Below that threshold, the data is too sparse for meaningful recommendations. Show popular and trending items as the fallback for new users.

Can I use machine learning for better recommendations?

Yes. For advanced personalization, use Google Cloud Recommendations AI or a custom ML model deployed as a Cloud Function. The category-matching approach in this tutorial is a strong baseline that works well for most apps.

How do I handle users who browse many categories?

Use the topCategory field calculated from the most frequently viewed category. For users with diverse interests, the recommendation function uses up to 3 top categories to provide variety.

Does the abandoned cart email comply with privacy regulations?

You need user consent to send marketing emails. Add a checkbox during signup for email communication preferences. Only send abandoned cart reminders to users who opted in. Include an unsubscribe link in every email.

Can I personalize search results too?

Yes. Boost products in the user's preferred categories in search results by adding a relevance score that weights category match. Products matching the user's topCategory appear higher in results.

Can RapidDev help build an advanced personalization engine?

Yes. RapidDev can implement collaborative filtering, content-based recommendation systems, A/B testing frameworks for personalization strategies, and real-time behavior analytics dashboards.

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.