Build a news aggregator that fetches articles from multiple RSS and API sources via a scheduled Cloud Function, normalizes them into a Firestore articles collection, and displays them in FlutterFlow with category TabBar navigation. Each tab shows a ListView of article cards sorted by publish date. Users can tap to read articles in an in-app WebView or open them externally. A bookmark feature saves articles to a user subcollection. The Cloud Function runs hourly, deduplicates by URL, and parses RSS XML into structured Firestore documents.
Building a News Aggregator Platform in FlutterFlow
This tutorial creates a news aggregator that pulls articles from multiple sources, organizes them by category, and presents them in a clean reading interface. Unlike a single-source RSS reader, this platform aggregates from multiple feeds, deduplicates articles, and stores them in Firestore for fast querying. Users browse by category, read articles in-app, and save favorites. This pattern works for industry news portals, content curation apps, and company internal news boards.
Prerequisites
- A FlutterFlow project with Firestore configured
- Firebase Cloud Functions enabled (Blaze plan)
- At least 3-4 RSS feed URLs to aggregate
- Basic understanding of TabBar and ListView in FlutterFlow
Step-by-step guide
Create the Firestore data model and identify RSS sources
Create the Firestore data model and identify RSS sources
Create an articles collection with fields: title (String), summary (String), imageUrl (String), sourceUrl (String, the original article link), sourceName (String, e.g., 'TechCrunch'), category (String: Tech, Business, Sports, Science, Entertainment), publishedAt (Timestamp), fetchedAt (Timestamp). Create a users/{uid}/saved_articles subcollection with fields: articleId (String), savedAt (Timestamp). Identify 3-4 RSS feed URLs for your categories. Store them in a feed_sources collection with fields: url (String), sourceName (String), category (String), isActive (bool).
Expected result: Firestore has articles, feed_sources, and saved_articles collections ready for the aggregation pipeline.
Build the Cloud Function to fetch and normalize RSS feeds
Build the Cloud Function to fetch and normalize RSS feeds
Create a scheduled Cloud Function that runs every hour. The function reads all active feed_sources documents, fetches each RSS feed URL using node-fetch or axios, parses the XML response using the xml2js package to extract title, link, description, pubDate, and any media content URL for the image. For each parsed entry, check if an article with the same sourceUrl already exists in Firestore to deduplicate. If it is new, create an articles document with the normalized fields including the source category. Set fetchedAt to the current server timestamp. Log the count of new articles added per run.
1// Cloud Function: fetchNewsFeeds (scheduled hourly)2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const axios = require('axios');5const { parseStringPromise } = require('xml2js');6admin.initializeApp();78exports.fetchNewsFeeds = functions.pubsub9 .schedule('every 1 hours')10 .onRun(async () => {11 const sourcesSnap = await admin.firestore()12 .collection('feed_sources').where('isActive', '==', true).get();1314 for (const sourceDoc of sourcesSnap.docs) {15 const { url, sourceName, category } = sourceDoc.data();16 try {17 const res = await axios.get(url, { timeout: 10000 });18 const parsed = await parseStringPromise(res.data);19 const items = parsed.rss?.channel?.[0]?.item || [];2021 for (const item of items.slice(0, 20)) {22 const sourceUrl = item.link?.[0] || '';23 const existing = await admin.firestore()24 .collection('articles')25 .where('sourceUrl', '==', sourceUrl).limit(1).get();2627 if (existing.empty && sourceUrl) {28 await admin.firestore().collection('articles').add({29 title: item.title?.[0] || '',30 summary: (item.description?.[0] || '').replace(/<[^>]*>/g, '').slice(0, 300),31 imageUrl: item['media:content']?.[0]?.$?.url || '',32 sourceUrl,33 sourceName,34 category,35 publishedAt: admin.firestore.Timestamp.fromDate(36 new Date(item.pubDate?.[0] || Date.now())37 ),38 fetchedAt: admin.firestore.FieldValue.serverTimestamp(),39 });40 }41 }42 } catch (e) {43 console.error(`Failed to fetch ${sourceName}: ${e.message}`);44 }45 }46 });Expected result: The Cloud Function fetches RSS feeds hourly, deduplicates articles, and stores new ones in Firestore.
Build the news feed with category TabBar and article cards
Build the news feed with category TabBar and article cards
Create a NewsPage. Add a TabBar with tabs for each category: Tech, Business, Sports, Science, Entertainment, and an All tab. Each tab contains a ListView bound to a Backend Query on the articles collection filtered by category (or no filter for All), ordered by publishedAt descending, with a limit of 20 documents. Each article card Container shows: the imageUrl on the left as a small thumbnail, the title in bold, the summary truncated to 2 lines, a Row with sourceName and a relative time label (e.g., '2 hours ago'). Add a pull-to-refresh RefreshIndicator that re-runs the query.
Expected result: Articles display in category tabs with thumbnails, titles, summaries, and source labels.
Add in-app article reading with WebView and bookmarks
Add in-app article reading with WebView and bookmarks
Tap an article card to navigate to an ArticleReaderPage with Route Parameter articleId. Query the article document. Display the title and source name at the top, then embed a Custom Widget WebView that loads the sourceUrl for the full article content. Add a bookmark IconButton in the AppBar. On tap, check if a saved_articles document exists for the current user and articleId. If not, create one with savedAt timestamp. If it exists, delete it. Fill the icon when bookmarked. Create a SavedArticlesPage that queries the user's saved_articles subcollection, joins each articleId to the articles collection, and displays them as the same card layout.
Expected result: Users can read articles in-app via WebView and bookmark favorites for later reading.
Add search and source management
Add search and source management
Add a search TextField at the top of the NewsPage above the TabBar. On text change, filter the current tab's query by title prefix match (where title >= searchTerm and title <= searchTerm + unicode high char). For admin users, create a SourceManagementPage that lists all feed_sources with their name, URL, category, and an isActive Switch toggle. Add a form to create new feed sources. Changes to feed sources take effect on the next hourly Cloud Function run. Display the last fetchedAt timestamp so admins know when articles were last updated.
Expected result: Users can search articles by title, and admins can manage RSS feed sources.
Complete working example
1FIRESTORE DATA MODEL:2 feed_sources/{sourceId}3 url: String (RSS feed URL)4 sourceName: String5 category: String (Tech / Business / Sports / Science / Entertainment)6 isActive: bool78 articles/{articleId}9 title: String10 summary: String (max 300 chars, HTML stripped)11 imageUrl: String12 sourceUrl: String (original article URL)13 sourceName: String14 category: String15 publishedAt: Timestamp16 fetchedAt: Timestamp1718 users/{uid}/saved_articles/{docId}19 articleId: String20 savedAt: Timestamp2122CLOUD FUNCTION: fetchNewsFeeds23 Schedule: every 1 hour24 Flow:25 1. Read active feed_sources26 2. For each source: fetch RSS URL → parse XML27 3. For each item: check dedup by sourceUrl28 4. If new: create articles doc with normalized fields2930PAGE: NewsPage31 WIDGET TREE:32 Column33 ├── TextField (search)34 ├── TabBar (All / Tech / Business / Sports / Science / Entertainment)35 └── TabBarView36 └── Per tab:37 RefreshIndicator38 ListView (articles, orderBy publishedAt desc, limit 20)39 └── ArticleCard Container40 Row41 ├── Image (thumbnail, 80x80)42 └── Column43 ├── Text (title, bold, max 2 lines)44 ├── Text (summary, max 2 lines)45 └── Row (sourceName + relative time)46 On Tap: navigate to ArticleReaderPage4748PAGE: ArticleReaderPage49 Route Parameter: articleId50 WIDGET TREE:51 Column52 ├── Row (title + bookmark IconButton)53 ├── Text (sourceName + publishedAt)54 └── Custom Widget: WebView (sourceUrl)5556PAGE: SavedArticlesPage57 Backend Query: saved_articles subcollection58 ListView (same ArticleCard layout)Common mistakes when designing a News Aggregator Platform in FlutterFlow
Why it's a problem: Fetching RSS feeds from the FlutterFlow client instead of a Cloud Function
How to avoid: Fetch feeds in a scheduled Cloud Function and store normalized articles in Firestore. The FlutterFlow client only reads from Firestore.
Why it's a problem: Not deduplicating articles by sourceUrl before writing to Firestore
How to avoid: Before creating an article document, query Firestore for an existing document with the same sourceUrl. Only write if no match exists.
Why it's a problem: Storing raw HTML from RSS descriptions without stripping tags
How to avoid: Strip HTML tags from the summary in the Cloud Function using a regex like replace(/<[^>]*>/g, '') before saving to Firestore.
Best practices
- Fetch and normalize RSS feeds server-side in a Cloud Function, never from the client
- Deduplicate articles by sourceUrl before writing to prevent duplicate entries
- Strip HTML tags from RSS descriptions for clean text display
- Use category-based TabBar for organized browsing with each tab querying independently
- Limit article queries to 20-30 items per page for fast loading
- Show relative timestamps like '2 hours ago' instead of absolute dates for freshness context
- Store feed sources in Firestore so admins can add or remove sources without code changes
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a news aggregator in FlutterFlow that fetches articles from RSS feeds using a scheduled Cloud Function, stores them in Firestore with deduplication, and displays them with category tabs, article cards, in-app WebView reading, and bookmarks. Give me the data model, Cloud Function code for RSS parsing, and FlutterFlow widget tree.
Create a news feed page with category tabs at the top (Tech, Business, Sports, Science, Entertainment), a list of article cards below each tab showing a thumbnail image, title, summary, and source name, and a floating bookmark icon on each card.
Frequently asked questions
How often should the Cloud Function fetch new articles?
Hourly is a good default. For breaking news sources, consider every 15 minutes. For slower-moving categories, every 4-6 hours. Balance freshness against Cloud Function invocation costs.
Can I add article images if the RSS feed does not include them?
Yes. In the Cloud Function, if media:content is empty, fetch the article page HTML and extract the Open Graph image meta tag. Store that URL as imageUrl. This adds processing time but improves the visual experience.
How do I handle feeds that require authentication?
Store API keys or credentials in Cloud Function environment variables. Pass the credentials as headers in the fetch request within the Cloud Function. Never expose credentials to the client.
Can users submit their own RSS feeds?
Yes. Add a form where users submit a feed URL. Validate the URL by attempting to parse it in a Cloud Function. If valid, add it to feed_sources with a review or auto-approve workflow.
How do I clean up old articles to manage Firestore costs?
Create a scheduled Cloud Function that deletes articles older than 30 days. Also delete corresponding saved_articles entries to prevent broken bookmarks. Run it daily during low-traffic hours.
Can RapidDev help build a full content aggregation platform?
Yes. RapidDev can build news platforms with ML-powered personalization, push notification alerts for breaking news, social sharing, reading analytics, and multi-language content support.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation