# How to Build a Personalized News Feed in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-35 min
- Compatibility: FlutterFlow Pro+ (Cloud Functions for relevance scoring)
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

### 2. 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.

### 3. 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.

```
// Cloud Function: computeUserFeedRanking
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

exports.computeUserFeedRanking = functions.pubsub
  .schedule('every 1 hours').onRun(async () => {
    const usersSnap = await admin.firestore()
      .collection('users').get();
    
    for (const userDoc of usersSnap.docs) {
      const interests = userDoc.data().interests || [];
      const articlesSnap = await admin.firestore()
        .collection('articles')
        .where('category', 'in', interests.slice(0, 10))
        .orderBy('publishedAt', 'desc')
        .limit(50).get();
      
      const batch = admin.firestore().batch();
      for (const article of articlesSnap.docs) {
        const score = calculateScore(article.data(), userDoc.data());
        const ref = userDoc.ref
          .collection('feed_ranked')
          .doc(article.id);
        batch.set(ref, { articleId: article.id, score });
      }
      await batch.commit();
    }
  });
```

**Expected result:** Users toggle between a relevance-ranked For You feed and a chronological Latest feed.

### 4. 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.

### 5. 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.

### 6. 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 code example

File: `FlutterFlow Personalized News Feed`

```text
FIRESTORE SCHEMA:
  articles (collection):
    title: String
    summary: String
    body: String
    category: String
    author: String
    thumbnailUrl: String
    publishedAt: Timestamp
  users (collection):
    interests: [String] (max 10)
    mutedTopics: [String]
  users/{uid}/feed_ranked (subcollection):
    articleId: String
    score: int
  user_engagements (collection):
    userId: String
    articleId: String
    eventType: String (view|like|share)
    category: String
    timestamp: Timestamp

PAGE: Onboarding — Interest Selection
  ChoiceChips (Tech, Science, Business, Sports, etc.)
  Max 10 selections
  Button "Continue" → update user doc interests array

PAGE: FeedPage
  ToggleButtons: For You | Latest
  Page State: feedMode (forYou | latest)

  FOR YOU MODE:
    Backend Query: users/{uid}/feed_ranked orderBy score desc
    → fetch article docs by articleId

  LATEST MODE:
    Backend Query: articles where category IN user.interests
    orderBy publishedAt desc
    Exclude: category NOT IN user.mutedTopics

  RefreshIndicator wrapping ListView
  Each article card:
    Image (thumbnail)
    Container (category chip)
    Text (headline)
    Text (summary, maxLines 2)
    Row: author + date + like IconButton + share IconButton
    On tap: navigate to ArticleDetail + log view engagement

SETTINGS: Topic Muting
  ListView of all categories
  Switch per category → add/remove from mutedTopics array

CLOUD FUNCTION: computeUserFeedRanking
  Runs hourly
  For each user: score articles by interest match + recency + engagement history
  Write top 50 to feed_ranked subcollection
```

## Common mistakes

- **Querying articles with whereIn using more than 10 interest values** — Firestore whereIn supports a maximum of 10 values. If a user selects 15 interests, the query fails silently and returns no results. Fix: Cap user interest selections at 10. If you need more, batch into groups of 10, run separate queries, and merge the results client-side.
- **Running the relevance scoring algorithm on the client** — Client-side scoring requires fetching all articles to score them, consuming excessive reads and battery. It also exposes your ranking algorithm to users. Fix: 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.
- **Showing an empty feed when a new user has not selected interests yet** — If interests is empty, a whereIn query with an empty array returns zero results. New users see a blank page and think the app is broken. Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-build-a-personalized-news-feed-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-build-a-personalized-news-feed-in-flutterflow
