Build a personalized news feed by storing user interests during onboarding via ChoiceChips, then querying articles matching those interests from Firestore. Add engagement tracking (reads, likes, shares) to refine relevance scoring in a Cloud Function. Users toggle between a For You feed ranked by relevance and a Latest feed sorted chronologically. Mute topics to exclude unwanted categories. Pull-to-refresh loads the newest articles on demand.
Building an Interest-Based Personalized Feed in FlutterFlow
A generic chronological feed shows the same content to everyone. A personalized feed ranks and filters articles by what each user cares about. This tutorial captures user interests, queries matching articles, tracks engagement to improve relevance, and lets users fine-tune their feed with topic muting.
Prerequisites
- FlutterFlow project with Firebase authentication
- Firestore articles collection with category and publishedAt fields
- Users collection with an interests array field
- Basic familiarity with Backend Queries in FlutterFlow
Step-by-step guide
Capture user interests during onboarding with ChoiceChips
Capture user interests during onboarding with ChoiceChips
On the onboarding page, add a ChoiceChips widget with options matching your article categories: Tech, Science, Business, Sports, Entertainment, Health, Politics. Set the ChoiceChips to allow multiple selection and bind the selected values to a Page State variable selectedInterests (list of strings). On the Continue button, update the current user's Firestore document to set the interests array field to selectedInterests. Also store this in App State so the feed can query it immediately without an extra Firestore read.
Expected result: Users select their preferred topics during onboarding and the choices are saved to their Firestore user document.
Build the personalized feed with interest-based filtering
Build the personalized feed with interest-based filtering
Create a FeedPage with a ListView bound to a Backend Query on the articles collection. Set the query to filter where category is in the current user's interests array, ordered by publishedAt descending. Each article card shows: an Image for the thumbnail, a Text for the category label (styled as a colored chip), the headline Text, a brief summary Text, and the author name with publish date. If the user has no interests set, show all articles sorted by publishedAt as a fallback.
Expected result: The feed shows only articles matching the user's selected interest categories, newest first.
Add For You vs Latest toggle with engagement scoring
Add For You vs Latest toggle with engagement scoring
Add ToggleButtons at the top of the feed page with two options: For You and Latest. Latest uses the basic chronological query from the previous step. For You requires a Cloud Function that pre-computes relevance scores. The function runs on a schedule or on article publish. It scores each article per user: interest match gets 10 points, recency within 24 hours gets 5 points, articles in categories the user has liked before get a bonus. Results are written to a users/{uid}/feed_ranked subcollection with articleId and score. The For You toggle queries this subcollection ordered by score descending.
1// Cloud Function: computeUserFeedRanking2const functions = require('firebase-functions');3const admin = require('firebase-admin');4admin.initializeApp();56exports.computeUserFeedRanking = functions.pubsub7 .schedule('every 1 hours').onRun(async () => {8 const usersSnap = await admin.firestore()9 .collection('users').get();10 11 for (const userDoc of usersSnap.docs) {12 const interests = userDoc.data().interests || [];13 const articlesSnap = await admin.firestore()14 .collection('articles')15 .where('category', 'in', interests.slice(0, 10))16 .orderBy('publishedAt', 'desc')17 .limit(50).get();18 19 const batch = admin.firestore().batch();20 for (const article of articlesSnap.docs) {21 const score = calculateScore(article.data(), userDoc.data());22 const ref = userDoc.ref23 .collection('feed_ranked')24 .doc(article.id);25 batch.set(ref, { articleId: article.id, score });26 }27 await batch.commit();28 }29 });Expected result: Users toggle between a relevance-ranked For You feed and a chronological Latest feed.
Track engagement signals for feed refinement
Track engagement signals for feed refinement
Create a Firestore collection user_engagements with fields: userId, articleId, eventType (view, like, share), category, timestamp. When a user taps an article to read it, create an engagement doc with eventType 'view'. Add a heart IconButton that toggles a like engagement. Add a share IconButton that logs a share event. The Cloud Function from the previous step reads these engagement docs to boost scores for categories the user interacts with most. For example, if a user reads 10 Tech articles but only 2 Sports articles, Tech articles get a higher relevance boost in future feed computations.
Expected result: User interactions (views, likes, shares) are tracked and influence future feed ranking calculations.
Implement topic muting to exclude unwanted categories
Implement topic muting to exclude unwanted categories
Add a settings section where users can mute specific topics. Create a Firestore field mutedTopics (array of strings) on the user document. On the feed page or in settings, show each category with a Mute/Unmute toggle (Switch widget). When a topic is muted, add it to the mutedTopics array. In the feed Backend Query, add a where clause excluding articles whose category is in mutedTopics. Alternatively, filter client-side if Firestore query limitations prevent combining whereIn with whereNotIn. This gives users control over their feed without changing their core interests.
Expected result: Muted topics are excluded from the feed, giving users fine-grained control over what they see.
Add pull-to-refresh for the latest articles
Add pull-to-refresh for the latest articles
Wrap the feed ListView in a RefreshIndicator widget. On refresh, re-run the Backend Query to fetch the latest articles from Firestore. Display a brief loading indicator while the query executes. This lets users manually check for new content without waiting for automatic updates. For the For You feed, the refresh triggers a re-read of the feed_ranked subcollection. For Latest, it re-runs the chronological query with the latest publishedAt results.
Expected result: Pulling down on the feed refreshes the article list with the most recent content.
Complete working example
1FIRESTORE SCHEMA:2 articles (collection):3 title: String4 summary: String5 body: String6 category: String7 author: String8 thumbnailUrl: String9 publishedAt: Timestamp10 users (collection):11 interests: [String] (max 10)12 mutedTopics: [String]13 users/{uid}/feed_ranked (subcollection):14 articleId: String15 score: int16 user_engagements (collection):17 userId: String18 articleId: String19 eventType: String (view|like|share)20 category: String21 timestamp: Timestamp2223PAGE: Onboarding — Interest Selection24 ChoiceChips (Tech, Science, Business, Sports, etc.)25 Max 10 selections26 Button "Continue" → update user doc interests array2728PAGE: FeedPage29 ToggleButtons: For You | Latest30 Page State: feedMode (forYou | latest)3132 FOR YOU MODE:33 Backend Query: users/{uid}/feed_ranked orderBy score desc34 → fetch article docs by articleId3536 LATEST MODE:37 Backend Query: articles where category IN user.interests38 orderBy publishedAt desc39 Exclude: category NOT IN user.mutedTopics4041 RefreshIndicator wrapping ListView42 Each article card:43 Image (thumbnail)44 Container (category chip)45 Text (headline)46 Text (summary, maxLines 2)47 Row: author + date + like IconButton + share IconButton48 On tap: navigate to ArticleDetail + log view engagement4950SETTINGS: Topic Muting51 ListView of all categories52 Switch per category → add/remove from mutedTopics array5354CLOUD FUNCTION: computeUserFeedRanking55 Runs hourly56 For each user: score articles by interest match + recency + engagement history57 Write top 50 to feed_ranked subcollectionCommon mistakes when building a Personalized News Feed in FlutterFlow
Why it's a problem: Querying articles with whereIn using more than 10 interest values
How to avoid: Cap user interest selections at 10. If you need more, batch into groups of 10, run separate queries, and merge the results client-side.
Why it's a problem: Running the relevance scoring algorithm on the client
How to avoid: Pre-compute relevance scores in a Cloud Function on a schedule or article publish trigger. The client just reads the pre-ranked feed_ranked subcollection.
Why it's a problem: Showing an empty feed when a new user has not selected interests yet
How to avoid: Add a fallback: if interests is empty or not set, query all articles ordered by publishedAt descending. Show a banner prompting the user to select interests for a personalized experience.
Best practices
- Cap user interests at 10 to stay within Firestore whereIn limits
- Pre-compute feed rankings server-side in Cloud Functions for performance
- Track engagement signals to continuously improve personalization
- Provide a Latest chronological option so users can escape the algorithm
- Allow topic muting for fine-grained feed control beyond core interests
- Show a fallback feed for new users before they select interests
- Use pull-to-refresh so users can manually check for new content
- Display category labels on article cards so users know why each article appears
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Build a personalized news feed in FlutterFlow. Users select interests during onboarding via ChoiceChips (max 10). Articles are filtered by interest categories from Firestore. A Cloud Function computes relevance scores using engagement data. The feed has For You (ranked) and Latest (chronological) toggles. Users can mute topics. Include the Firestore schema and ranking function.
Create a feed page with ToggleButtons at the top (For You and Latest), a ListView below showing article cards with thumbnail image, category chip, headline, summary, and a like button. Add a RefreshIndicator wrapper.
Frequently asked questions
How many interest categories should I offer?
Keep it between 8 and 15 categories. Fewer than 8 feels limiting; more than 15 overwhelms users during onboarding. Remember Firestore whereIn supports a maximum of 10 selected values.
Can I update interests after onboarding?
Yes. Add an Edit Interests button on the settings or profile page that opens the same ChoiceChips interface. Update the user document on save and re-trigger the feed ranking Cloud Function.
How often should the ranking Cloud Function run?
Hourly is a good starting point. For high-volume apps, run it on each new article publish using a Firestore trigger. For low-volume apps, every few hours is sufficient.
Can I add a trending section alongside personalized content?
Yes. Query articles from the past 24 hours ordered by a combined engagement count (views plus likes). Display the top five as a Trending horizontal carousel above the main feed.
How do I handle articles in categories the user has not selected?
By default they are excluded from the For You feed. The Latest feed can optionally show all categories. You can also add a Discover section showing popular articles outside the user's interests to encourage exploration.
Can RapidDev help build a personalized content platform?
Yes. RapidDev can build recommendation engines, personalized feeds, engagement tracking, and content management systems tailored to your audience and content types.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation