# How to Create a News Feed Algorithm in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-35 min
- Compatibility: FlutterFlow Free+
- Last updated: March 2026

## TL;DR

Create a feed ranking algorithm that scores each post using a weighted formula combining recency decay, engagement metrics, and author affinity. A scheduled Cloud Function computes scores and writes ranked results to each user's personal ranked_feed subcollection. The FlutterFlow client simply reads the pre-computed feed for instant display. Store algorithm weights in a Firestore config document so you can tune ranking without redeploying code.

## Building a Smart Feed Ranking Algorithm in FlutterFlow

A chronological feed quickly becomes unusable as content grows. This tutorial builds the ranking algorithm behind a For You feed. Posts are scored by recency, engagement, and how much the user interacts with each author. A Cloud Function pre-computes the ranked feed per user, so the client reads a simple sorted list with zero computation.

## Before you start

- A FlutterFlow project with Firestore and authentication configured
- A posts collection with fields for likes, comments, shares, and authorId
- Cloud Functions environment configured
- At least 10 sample posts from multiple authors for testing

## Step-by-step guide

### 1. Design the Firestore data model for ranked feeds and algorithm config

Create a ranked_feed subcollection under each user document: users/{uid}/ranked_feed/{postId} with fields: postId (String), score (Double), title (String), authorName (String), thumbnailUrl (String), engagementCount (Integer), createdAt (Timestamp). Also create an algorithm_config document at the root level with fields: recency_weight (Double, default 0.4), engagement_weight (Double, default 0.35), affinity_weight (Double, default 0.25), decay_half_life_hours (Integer, default 24). This config document lets you tune the algorithm without changing code.

**Expected result:** Firestore has a ranked_feed subcollection per user and a tunable algorithm_config document.

### 2. Build the scoring Cloud Function with weighted formula

Create a Cloud Function that runs on a schedule (every 15 minutes). For each active user, it fetches all posts from the last 7 days and scores each one. The formula: score = (recency_weight * decay_function) + (engagement_weight * normalized_engagement) + (affinity_weight * author_interaction_score). The decay function uses exponential decay: 1 / (1 + age_hours / decay_half_life_hours). Normalized engagement = (likes + comments*2 + shares*3) / max_engagement_in_batch. Author interaction score = count of user's likes and comments on that author's previous posts / total_interactions. Apply a diversity penalty of -0.1 if the previous post in the ranked list is from the same author.

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

exports.rankFeed = functions.pubsub
  .schedule('every 15 minutes').onRun(async () => {
    const db = admin.firestore();
    const config = (await db.doc('algorithm_config/default').get()).data();
    const { recency_weight, engagement_weight,
            affinity_weight, decay_half_life_hours } = config;
    const now = Date.now();
    const weekAgo = new Date(now - 7*24*60*60*1000);
    const posts = await db.collection('posts')
      .where('createdAt', '>=', weekAgo).get();
    const maxEng = Math.max(...posts.docs.map(p => {
      const d = p.data();
      return d.likes + d.comments*2 + (d.shares||0)*3;
    }), 1);
    const users = await db.collection('users')
      .where('lastActive', '>=', weekAgo).get();
    for (const user of users.docs) {
      const uid = user.id;
      const interactions = await db
        .collection('interactions')
        .where('userId', '==', uid).get();
      const authorScores = {};
      let totalInt = Math.max(interactions.size, 1);
      interactions.forEach(i => {
        const a = i.data().authorId;
        authorScores[a] = (authorScores[a]||0) + 1;
      });
      const scored = posts.docs.map(p => {
        const d = p.data();
        const ageH = (now - d.createdAt.toMillis())
                     / 3600000;
        const recency = 1 / (1 + ageH / decay_half_life_hours);
        const eng = (d.likes + d.comments*2
                     + (d.shares||0)*3) / maxEng;
        const affinity = (authorScores[d.authorId]||0)
                         / totalInt;
        return {
          postId: p.id, title: d.title,
          authorName: d.authorName,
          thumbnailUrl: d.thumbnailUrl || '',
          engagementCount: d.likes + d.comments,
          createdAt: d.createdAt,
          score: recency_weight * recency
               + engagement_weight * eng
               + affinity_weight * affinity
        };
      }).sort((a,b) => b.score - a.score);
      // Diversity penalty
      for (let i = 1; i < scored.length; i++) {
        if (scored[i].authorName === scored[i-1].authorName)
          scored[i].score -= 0.1;
      }
      scored.sort((a,b) => b.score - a.score);
      const batch = db.batch();
      const feedRef = db.collection(`users/${uid}/ranked_feed`);
      const old = await feedRef.get();
      old.forEach(d => batch.delete(d.ref));
      scored.slice(0, 50).forEach(s => {
        batch.set(feedRef.doc(s.postId), s);
      });
      await batch.commit();
    }
  });
```

**Expected result:** Cloud Function runs every 15 minutes and writes the top 50 scored posts to each active user's ranked_feed subcollection.

### 3. Create an admin page to tune algorithm weights in real time

Build an AlgorithmConfigPage accessible to admin users. Add three Slider widgets labeled Recency Weight, Engagement Weight, and Affinity Weight, each ranging from 0.0 to 1.0. Bind their initial values to a Backend Query on algorithm_config/default. Add a TextField for decay_half_life_hours. Include a Save button that updates the algorithm_config document with the new values. Add a note Text widget reminding that changes take effect on the next Cloud Function run (within 15 minutes). This lets you tune ranking live without code changes.

**Expected result:** Admins can adjust ranking weights via sliders and see changes reflected in the feed within 15 minutes.

### 4. Build the For You feed page with ranked posts

Create a FeedPage with ChoiceChips at the top: For You and Latest. For the For You tab, add a ListView with a Backend Query on users/{currentUser.uid}/ranked_feed ordered by score descending. For the Latest tab, query the posts collection ordered by createdAt descending. Each list item is a PostCard Container with: thumbnail Image, title Text, author name Text, engagement count with icons, and relative timestamp. Tapping a card navigates to the full post detail page with the postId as a route parameter.

**Expected result:** Users see a ranked For You feed by default with the option to toggle to a chronological Latest feed.

### 5. Track user interactions to improve affinity scoring

Create an interactions collection with fields: userId, postId, authorId, type (String: 'like', 'comment', 'share'), timestamp. When a user likes, comments, or shares a post, create an interaction document via an Action Flow. These interactions feed the affinity score in the ranking algorithm. The more a user interacts with a specific author's content, the higher that author's posts rank in their feed. The Cloud Function reads these interactions to calculate author_interaction_score per user.

**Expected result:** User interactions are tracked in Firestore and used by the ranking algorithm to personalize feed ordering by author affinity.

## Complete code example

File: `FlutterFlow News Feed Algorithm Setup`

```text
FIRESTORE DATA MODEL:
  posts/{postId}
    title: String
    authorId: String
    authorName: String
    thumbnailUrl: String
    likes: Integer
    comments: Integer
    shares: Integer
    createdAt: Timestamp

  users/{uid}/ranked_feed/{postId}
    postId: String
    score: Double
    title: String
    authorName: String
    thumbnailUrl: String
    engagementCount: Integer
    createdAt: Timestamp

  interactions/{interactionId}
    userId: String
    postId: String
    authorId: String
    type: 'like' | 'comment' | 'share'
    timestamp: Timestamp

  algorithm_config/default
    recency_weight: 0.4
    engagement_weight: 0.35
    affinity_weight: 0.25
    decay_half_life_hours: 24

SCORING FORMULA:
  score = (recency_weight * decay) + (engagement_weight * norm_eng)
        + (affinity_weight * author_affinity)
  decay = 1 / (1 + age_hours / decay_half_life_hours)
  norm_eng = (likes + comments*2 + shares*3) / max_eng
  author_affinity = user_interactions_with_author / total_interactions
  diversity_penalty = -0.1 if same author as previous ranked post

PAGE: FeedPage
  WIDGET TREE:
    Column
      ├── ChoiceChips (For You | Latest)
      └── Expanded
            └── ListView
                  For You: Backend Query → ranked_feed, order by score desc
                  Latest: Backend Query → posts, order by createdAt desc
                  → PostCard Component

  PostCard:
    Container (padding, border radius)
      Row
        ├── Image (thumbnailUrl, 80x80)
        └── Column
              ├── Text (title, bold, maxLines: 2)
              ├── Text (authorName, grey)
              └── Row
                    ├── Icon(heart) + Text(engagementCount)
                    └── Text (relative timestamp)
```

## Common mistakes

- **Recalculating feed ranking on every user page load** — Scoring all posts against all user interactions on each request is expensive and slow, causing noticeable loading delays as the content library grows. Fix: Pre-compute ranked feeds via a scheduled Cloud Function (every 15 minutes) and store results. The client reads the pre-computed list instantly.
- **Hardcoding algorithm weights in the Cloud Function** — Every ranking adjustment requires a code change and redeployment. You cannot A/B test or respond quickly to user feedback. Fix: Store weights in a Firestore algorithm_config document. The Cloud Function reads them at runtime. Admins adjust weights via a UI with immediate effect.
- **Not applying a diversity penalty for consecutive posts by the same author** — A prolific author with high engagement dominates the entire feed, pushing all other content out of view. Users see the same voice repeatedly. Fix: Apply a diversity penalty (subtract 0.1 from score) when consecutive posts in the ranked list share the same author, then re-sort.

## Best practices

- Pre-compute ranked feeds on a schedule rather than calculating on every page load
- Store algorithm weights in Firestore for live tuning without code redeployment
- Apply diversity penalties to prevent any single author from dominating the feed
- Normalize engagement scores relative to the batch maximum for fair comparison
- Use exponential decay for recency so older posts gracefully lose ranking
- Track user interactions (likes, comments, shares) to build author affinity data
- Limit ranked feed to top 50 posts per user to control Firestore storage costs
- Provide a chronological Latest toggle so users can bypass the algorithm

## Frequently asked questions

### How often should the ranking Cloud Function run?

Every 15 minutes is a good balance between freshness and cost. For high-volume apps with rapidly changing content, you can reduce to every 5 minutes. For lower volume, every hour works fine.

### Can I A/B test different algorithm configurations?

Yes. Create multiple algorithm_config documents (variant_a, variant_b). Assign users to variants randomly. The Cloud Function reads the user's assigned config and applies those weights. Track engagement metrics per variant.

### What happens for new users with no interaction history?

When a user has zero interactions, the affinity component is zero for all authors. The feed ranks purely on recency and engagement, effectively showing popular recent content. As the user interacts, personalization kicks in gradually.

### How do I prevent the algorithm from creating filter bubbles?

Inject a small percentage of random or diverse posts (from unfollowed authors, different categories) into the ranked feed. This exploration component exposes users to new content beyond their interaction patterns.

### Will pre-computing feeds for thousands of users be expensive?

Only compute for active users (those who logged in within the past 7 days). For 1,000 active users with 100 posts each, that is 100,000 score calculations every 15 minutes, which is manageable on standard Cloud Function capacity.

### Can RapidDev help build a custom feed ranking system?

Yes. RapidDev can implement advanced ranking algorithms with machine learning models, real-time personalization, A/B testing infrastructure, and analytics dashboards to monitor feed quality.

---

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