Build a live sports ticker in FlutterFlow by connecting to a sports data API (SportsData.io, ESPN, or TheSportsDB) via a Cloud Function that polls for scores every 60 seconds and writes results to Firestore. FlutterFlow reads from Firestore with a real-time listener, displaying active game scores in a horizontally scrolling ticker. Never poll the sports API from each user's device — one Cloud Function polls for all users and writes to a shared Firestore document.
Real-time sports scores via scheduled Cloud Functions and Firestore real-time listeners
A live sports ticker requires fresh data from a sports data provider — SportsData.io, ESPN, or TheSportsDB all offer APIs with current scores, play-by-play events, standings, and statistics. The key architecture decision is never polling the sports API from each user's device. With 1,000 concurrent users all refreshing scores independently, you exhaust your API quota in minutes and face massive overage charges. Instead, a single scheduled Cloud Function polls the API every 60 seconds during game time and writes to Firestore. All FlutterFlow users read from the same Firestore documents with real-time listeners — one API call serves unlimited users simultaneously.
Prerequisites
- A FlutterFlow project with Firebase configured (Settings → Project Setup → Firebase)
- A SportsData.io API key (free trial available at sportsdata.io) or TheSportsDB free API
- Cloud Functions enabled on Firebase (Blaze plan required for scheduled functions)
- Google Cloud Scheduler enabled in your Firebase project
Step-by-step guide
Choose a sports data API and understand its data structure
Choose a sports data API and understand its data structure
TheSportsDB (thesportsdb.com/api.php) is completely free and requires no API key for basic data — ideal for getting started. SportsData.io provides more comprehensive data including play-by-play and real-time scores but requires paid API access. The ESPN API has no official developer documentation but has well-known unofficial endpoints (site.api.espn.com/apis/site/v2/sports/{sport}/{league}/scoreboard) that are widely used for non-commercial projects. For this tutorial, we use SportsData.io's NFL endpoint as a concrete example. Key endpoints: GET /v3/nfl/scores/{season}/ScoresByWeek/{week} returns current-week scores with homeScore, awayScore, quarter, timeRemaining, status (Scheduled/InProgress/Final). Plan your Firestore structure to match this data.
Expected result: You understand the API response structure and have created a matching Firestore schema for storing games, scores, and events.
Create the Firestore schema for sports data
Create the Firestore schema for sports data
Create a sports_scores Firestore collection. Each document represents one game with fields: gameId (String), sport (String), league (String), homeTeam (String), awayTeam (String), homeScore (Integer), awayScore (Integer), status (String — Scheduled/InProgress/Final), quarter (String), timeRemaining (String), startTime (Timestamp), venue (String), lastUpdated (Timestamp). Create a ticker_config document in a settings collection that controls which sports are displayed and what polling interval to use. Create a play_by_play subcollection under each game document with events: description, team, quarter, timeRemaining, recordedAt. This subcollection allows the game detail page to show a running play list.
Expected result: Firestore has the sports_scores collection and schema ready to receive data from the Cloud Function.
Create the score-polling scheduled Cloud Function
Create the score-polling scheduled Cloud Function
Create a Cloud Function triggered by Cloud Scheduler named updateSportsScores. In Cloud Functions → Triggers → Cloud Scheduler, set the schedule to every 1 minute (cron: * * * * *) but only activate during typical game hours (e.g., Thursday-Monday for NFL). The function calls the SportsData.io scores endpoint with your API key. For each game in the response, update the corresponding Firestore sports_scores document using admin.firestore().collection('sports_scores').doc(gameId).set({...scoreData}, {merge: true}). This merge:true ensures only changed fields are updated, not the entire document. Add logic to detect status changes (InProgress → Final) and trigger push notifications for users following those teams.
1const functions = require('firebase-functions');2const admin = require('firebase-admin');3const axios = require('axios');4admin.initializeApp();56const API_KEY = functions.config().sportsdata.key;78exports.updateSportsScores = functions.pubsub9 .schedule('every 1 minutes')10 .onRun(async () => {11 const url = `https://api.sportsdata.io/v3/nfl/scores/json/ScoresByWeek/2025/1`;12 const { data } = await axios.get(url, {13 headers: { 'Ocp-Apim-Subscription-Key': API_KEY },14 });1516 const db = admin.firestore();17 const batch = db.batch();1819 for (const game of data) {20 const ref = db.collection('sports_scores').doc(String(game.ScoreID));21 batch.set(ref, {22 gameId: String(game.ScoreID),23 sport: 'NFL',24 homeTeam: game.HomeTeam,25 awayTeam: game.AwayTeam,26 homeScore: game.HomeScore || 0,27 awayScore: game.AwayScore || 0,28 status: game.Status,29 quarter: game.Quarter ? `Q${game.Quarter}` : null,30 timeRemaining: game.TimeRemaining,31 startTime: new Date(game.DateTime),32 lastUpdated: admin.firestore.FieldValue.serverTimestamp(),33 }, { merge: true });34 }3536 await batch.commit();37 return null;38 });Expected result: Sports scores in Firestore update every 60 seconds from a single Cloud Function call, regardless of how many users are watching the ticker.
Build the horizontally scrolling ticker in FlutterFlow
Build the horizontally scrolling ticker in FlutterFlow
On your home page or sports tab, add a Container at the top for the ticker. Inside it, add a ListView with scroll direction set to Horizontal. Add a Backend Query to the ListView pointing to the sports_scores collection filtered by status == InProgress (showing only live games). Turn off Single Time Query so it is a real-time listener — scores update instantly as Firestore changes. Set Generate Dynamic Children on the ListView. Each child is a Container (fixed width 200px) showing: home team abbreviation, away team abbreviation, score (homeScore vs awayScore), and the current quarter and time (Q3 2:45). Add a Timer widget or AnimatedContainer to make the ListView auto-scroll continuously using a Custom Action that increments the scroll controller position every 3 seconds.
Expected result: A horizontally scrolling ticker at the top of the page shows all in-progress games with live scores, updating automatically as Firestore receives new data from the Cloud Function.
Build the game detail page with standings and play-by-play
Build the game detail page with standings and play-by-play
Create a GameDetail page with a Page Parameter gameId (String). Add a Backend Query loading the game document. Show the full score header: team names, logos (from an assets map or URL), quarter, and time remaining. Below the score, add a TabBar with three tabs: (1) Play-by-Play: a Backend Query on the play_by_play subcollection ordered by recordedAt descending, displayed as a ListView of play descriptions. (2) Box Score: a Container with a table layout showing stats from the game document's stats Map field if your API provides them. (3) Standings: a Backend Query on a team_standings collection showing the league table sorted by wins. Add a follow toggle button — when toggled, add the gameId to the user's followedGames array in Firestore so the notification logic can target them.
Expected result: The game detail page shows the full match experience with real-time play-by-play, stats, and the option to follow a game for score change notifications.
Complete working example
1Firestore Schema:2sports_scores/{gameId}3├── gameId: String4├── sport: String (NFL / NBA / MLB / Soccer)5├── league: String6├── homeTeam: String7├── awayTeam: String8├── homeScore: Integer9├── awayScore: Integer10├── status: String (Scheduled / InProgress / Final)11├── quarter: String (Q1/Q2/Q3/Q4/OT)12├── timeRemaining: String13├── startTime: Timestamp14├── venue: String15├── lastUpdated: Timestamp16└── play_by_play (subcollection)17 └── {eventId}18 ├── description: String19 ├── team: String20 ├── quarter: String21 ├── timeRemaining: String22 └── recordedAt: Timestamp2324FlutterFlow Components:2526Ticker (on HomePage)27├── Container (fixed height: 60px, background: dark)28└── ListView (horizontal, real-time Backend Query: status == InProgress)29 └── GameCard (200px wide)30 ├── Row: awayTeam + awayScore31 ├── Text: 'vs'32 ├── Row: homeTeam + homeScore33 └── Text: quarter + timeRemaining3435GameDetail Page (param: gameId)36├── Backend Query: sports_scores/{gameId} (real-time)37├── ScoreHeader: team names + logos + score + quarter38└── TabBar:39 ├── Tab 1: Play-by-Play40 │ └── ListView (Backend Query: play_by_play, realtime)41 ├── Tab 2: Stats Table42 └── Tab 3: Standings4344Cloud Function: updateSportsScores45├── Trigger: Cloud Scheduler (every 1 min)46├── Fetches: SportsData.io scores endpoint47├── Writes: sports_scores/{gameId} (merge)48└── Notifies: users following changed-status games via FCMCommon mistakes
Why it's a problem: Polling the sports API from each user's device on page load or on a timer
How to avoid: Use ONE scheduled Cloud Function that polls for all users combined. The Cloud Function writes to Firestore, and all clients read from Firestore — one API call serves unlimited concurrent users.
Why it's a problem: Using real-time Backend Queries for all data on the ticker page including completed games
How to avoid: Filter the real-time ticker query to InProgress games only. Use Single Time Query for completed game details and standings — they update infrequently and do not need persistent listeners.
Why it's a problem: Storing team logos as hardcoded image URLs from a third-party CDN
How to avoid: Download team logos once and upload them to Firebase Storage. Store the permanent Firebase Storage URLs in a Firestore teams collection. Reference them by team abbreviation from your score data.
Best practices
- Pause Cloud Scheduler polling during the off-season or when no games are scheduled to avoid unnecessary API calls and Cloud Function costs
- Add a sport selector (NFL, NBA, MLB, Soccer) so users can filter the ticker to sports they follow — store the preference in App State or the user's Firestore profile
- Include a last-updated timestamp visible to users so they know how fresh the scores are — 'Updated 45 seconds ago' manages expectations
- Handle the pre-game state gracefully — show kickoff time and team records for Scheduled games rather than blank scores
- Cache team names and logos in Firestore so the ticker renders instantly even before the first Cloud Function run after app start
- Use batch writes in the Cloud Function to update all game documents atomically — this is more efficient than individual document updates
- Test the real-time update behavior by manually updating a score in Firebase Console and watching the ticker update in the running FlutterFlow app
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow sports app that needs live scores. Write a Firebase Cloud Function in Node.js that: (1) is triggered by Cloud Scheduler every 1 minute, (2) calls the SportsData.io NFL scores API endpoint for the current week using the API key from Firebase config, (3) batch-writes the game data to a Firestore sports_scores collection with merge:true so only changed fields are updated, and (4) sends FCM push notifications to users following any game whose status just changed from InProgress to Final. Include error handling and the Firestore batch write pattern.
Create a live sports ticker in my FlutterFlow app at the top of the home page. It should be a horizontally scrolling ListView showing in-progress games from Firestore's sports_scores collection filtered by status == InProgress. Each card should show both teams, their scores, and the current quarter. The ListView should use a real-time Backend Query so scores update automatically. Make the ticker auto-scroll slowly from left to right.
Frequently asked questions
Which sports data API should I use for a FlutterFlow sports ticker?
TheSportsDB (thesportsdb.com) is completely free and works well for schedules, team info, and historical results. For live in-progress scores with real-time updates, SportsData.io ($9/month per sport) and SportRadar (enterprise pricing) are the top choices. The ESPN unofficial API works for hobbyist projects but has no guarantees of availability or support.
Can I show scores for multiple sports (NFL, NBA, Soccer) in the same ticker?
Yes. Query the sports_scores collection without a sport filter to get all active games, or add a sport field to your query as a Where filter and let users select their sport. If using multiple sports APIs, each gets its own Cloud Function writing to the same sports_scores collection with a sport field on each document.
How do I make the ticker scroll automatically without user interaction?
Create a Custom Action using a ScrollController that advances the scroll position every 3-4 seconds using scrollController.animateTo(position + 200, duration: 500ms, curve: Curves.linear). Attach this as a periodic action using a Timer. When the scroll reaches the end, reset to position 0 to create an infinite loop effect.
How do I add push notifications when a team scores or a game ends?
In the Cloud Function that polls scores, compare the new score to the previous Firestore value before writing. If homeScore or awayScore increased, or if status changed to Final, query users who have the team in their followedTeams array and send them an FCM notification via admin.messaging().sendMulticast().
Does the sports ticker work offline if the user has no internet connection?
Firestore's offline persistence (enabled by default on mobile) caches the last-read data locally. The ticker will show the most recently cached scores when offline but will not update. Show a connection status indicator so users know they are seeing potentially stale data. The ticker resumes live updates automatically when connectivity is restored.
What if I need a full sports app with betting features, fantasy leagues, and multiple leagues?
A comprehensive sports app requires multiple API integrations, complex Firestore schemas, user preference systems, and significant real-time architecture. RapidDev has built production sports applications in FlutterFlow and can design the complete data architecture for your requirements.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation