Sentiment analysis automatically scores user feedback as positive, negative, or neutral so you can prioritize what to fix. Connect a Cloud Function to Google Cloud Natural Language API's analyzeSentiment endpoint, store the score and magnitude in Firestore, then display trend charts in an admin dashboard. Set up alerts when the average sentiment score drops below a threshold.
From raw feedback to actionable sentiment data
Star ratings tell you how happy users are in aggregate, but they don't tell you why. A 3-star review with 'checkout was confusing but products are great' contains two distinct signals. Google Cloud Natural Language API's analyzeSentiment endpoint returns a score (–1.0 to +1.0) and a magnitude (0 to infinity). Score indicates polarity; magnitude indicates how strongly the text expresses that emotion. A score of –0.8 with high magnitude means strongly negative and confident. By storing these values in Firestore alongside every feedback submission, you build a queryable sentiment history that powers admin charts, product prioritization, and automated alerts. The Cloud Function pattern keeps your API key server-side and adds analysis without changing your existing feedback submission flow.
Prerequisites
- FlutterFlow project connected to Firebase with Firestore enabled
- Google Cloud project with Natural Language API enabled and a service account key
- Firebase Cloud Functions set up (firebase init functions in your project)
- An existing feedback collection in Firestore or a form to create one
- Basic familiarity with FlutterFlow Action Flows
Step-by-step guide
Enable Google Cloud Natural Language API and get credentials
Enable Google Cloud Natural Language API and get credentials
Go to console.cloud.google.com and select your project. In the search bar type 'Natural Language API' and click Enable. Next, go to APIs & Services → Credentials → Create Credentials → API Key. Copy the key. For production, restrict the key to the Natural Language API only by clicking Edit on the key and setting API restrictions. Store this key as a Firebase environment variable: in your terminal run 'firebase functions:config:set nlp.key=YOUR_API_KEY'. This keeps the key off the client and out of your codebase.
Expected result: Natural Language API shows as 'Enabled' in your Google Cloud console. API key is stored as a Firebase config variable.
Write a Cloud Function that analyzes sentiment and updates Firestore
Write a Cloud Function that analyzes sentiment and updates Firestore
Create a new Cloud Function that triggers when a document is written to your feedback collection. It reads the comment text, calls the Natural Language API, extracts the score and magnitude, then writes those values back to the same Firestore document. This is a Firestore onWrite trigger — it fires automatically every time a feedback document is created or updated, so you never need to call it manually from your app. Deploy with 'firebase deploy --only functions'.
1// functions/index.js2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const axios = require('axios');56admin.initializeApp();78exports.analyzeFeedbackSentiment = functions.firestore9 .document('feedback/{feedbackId}')10 .onCreate(async (snap, context) => {11 const data = snap.data();12 const text = data.comment;13 if (!text || text.trim().length === 0) return null;1415 const apiKey = functions.config().nlp.key;16 const url = `https://language.googleapis.com/v1/documents:analyzeSentiment?key=${apiKey}`;1718 try {19 const response = await axios.post(url, {20 document: { type: 'PLAIN_TEXT', content: text },21 encodingType: 'UTF8',22 });2324 const sentiment = response.data.documentSentiment;25 const score = sentiment.score; // -1.0 to +1.026 const magnitude = sentiment.magnitude; // 0 to infinity2728 // Classify for easy filtering29 let label = 'neutral';30 if (score >= 0.25) label = 'positive';31 if (score <= -0.25) label = 'negative';3233 await snap.ref.update({34 sentiment_score: score,35 sentiment_magnitude: magnitude,36 sentiment_label: label,37 analyzed_at: admin.firestore.FieldValue.serverTimestamp(),38 });3940 console.log(`Feedback ${context.params.feedbackId}: ${label} (${score})`);41 return null;42 } catch (error) {43 console.error('Sentiment analysis failed:', error.message);44 return null;45 }46 });Expected result: After deploying, submit a test feedback document in Firestore. Within 5-10 seconds it should gain sentiment_score, sentiment_magnitude, sentiment_label, and analyzed_at fields.
Update the FlutterFlow feedback form to write to Firestore
Update the FlutterFlow feedback form to write to Firestore
In FlutterFlow, open your feedback page. Make sure it has a TextField for the comment and a Button to submit. In the Button's Action Flow, add a Firestore Create Document action targeting your 'feedback' collection. Map the TextField value to the 'comment' field. Also store the current user's UID (from Authenticated User → uid) and a timestamp (from Set from Variable → Current Time). You do NOT need to include sentiment fields — the Cloud Function populates those automatically after the document is created. Add a SnackBar action after the create action to show 'Thanks for your feedback!'.
Expected result: Submitting the form creates a new document in the feedback collection. The Cloud Function enriches it with sentiment data within seconds.
Build the admin sentiment dashboard with charts
Build the admin sentiment dashboard with charts
Create a new page called 'Admin Dashboard' in FlutterFlow. Add a Backend Query at the page level that fetches all documents from the feedback collection, ordered by analyzed_at descending. Add three Count widgets showing the number of documents where sentiment_label equals 'positive', 'negative', and 'neutral' respectively — use Firestore Query conditions to filter. Add a Bar Chart widget: set the X axis to the submission date (grouped by day) and the Y axis to the average sentiment_score. Finally, add a ListView below the chart showing recent feedback cards with the comment text and a color-coded sentiment badge (green, red, neutral).
Expected result: Admin dashboard shows live counts of positive/negative/neutral feedback and a chart of sentiment over time, updating as new feedback arrives.
Add automated alerts when negative feedback spikes
Add automated alerts when negative feedback spikes
Extend your Cloud Function to send an alert email when the rolling negative sentiment ratio exceeds a threshold. After writing the sentiment data back to Firestore, query the last 50 feedback documents and calculate the percentage that are 'negative'. If it exceeds 30%, send an alert using the Firebase Transactional Email extension or a simple SendGrid API call. Add a cooldown check by storing the last alert time in a Firestore config document so you don't flood your inbox.
1// Add inside analyzeFeedbackSentiment, after the snap.ref.update() call:2const db = admin.firestore();3const recentDocs = await db.collection('feedback')4 .orderBy('analyzed_at', 'desc')5 .limit(50)6 .get();78const analyzed = recentDocs.docs.filter(d => d.data().sentiment_label);9const negCount = analyzed.filter(d => d.data().sentiment_label === 'negative').length;10const negRatio = negCount / analyzed.length;1112if (negRatio > 0.30) {13 const configRef = db.doc('config/sentiment_alerts');14 const config = await configRef.get();15 const lastAlert = config.data()?.last_alert_at?.toMillis() || 0;16 const oneHour = 60 * 60 * 1000;1718 if (Date.now() - lastAlert > oneHour) {19 // Send alert via your preferred email service20 console.warn(`ALERT: ${Math.round(negRatio * 100)}% negative sentiment in last 50 reviews`);21 await configRef.set({ last_alert_at: admin.firestore.FieldValue.serverTimestamp() }, { merge: true });22 }23}Expected result: When 30%+ of recent feedback is negative, an alert is logged (or emailed) and a cooldown prevents repeated alerts within one hour.
Complete working example
1// Firebase Cloud Function: Sentiment Analysis on User Feedback2// Deploy: firebase deploy --only functions3// Config: firebase functions:config:set nlp.key=YOUR_GOOGLE_NLP_API_KEY45const functions = require('firebase-functions');6const admin = require('firebase-admin');7const axios = require('axios');89admin.initializeApp();1011exports.analyzeFeedbackSentiment = functions.firestore12 .document('feedback/{feedbackId}')13 .onCreate(async (snap, context) => {14 const data = snap.data();15 const text = data.comment;1617 if (!text || text.trim().length < 3) return null;1819 const apiKey = functions.config().nlp.key;20 const url =21 `https://language.googleapis.com/v1/documents:analyzeSentiment?key=${apiKey}`;2223 try {24 const response = await axios.post(url, {25 document: { type: 'PLAIN_TEXT', content: text },26 encodingType: 'UTF8',27 });2829 const { score, magnitude } = response.data.documentSentiment;3031 let label = 'neutral';32 if (score >= 0.25) label = 'positive';33 if (score <= -0.25) label = 'negative';3435 await snap.ref.update({36 sentiment_score: score,37 sentiment_magnitude: magnitude,38 sentiment_label: label,39 analyzed_at: admin.firestore.FieldValue.serverTimestamp(),40 });4142 // Check for negative spike alert43 const db = admin.firestore();44 const recentDocs = await db.collection('feedback')45 .orderBy('analyzed_at', 'desc')46 .limit(50)47 .get();4849 const analyzed = recentDocs.docs.filter(d => d.data().sentiment_label);50 if (analyzed.length >= 10) {51 const negCount = analyzed.filter(52 d => d.data().sentiment_label === 'negative'53 ).length;54 const negRatio = negCount / analyzed.length;5556 if (negRatio > 0.30) {57 const configRef = db.doc('config/sentiment_alerts');58 const config = await configRef.get();59 const lastAlert = config.data()?.last_alert_at?.toMillis() || 0;60 const oneHour = 60 * 60 * 1000;6162 if (Date.now() - lastAlert > oneHour) {63 console.warn(64 `SENTIMENT ALERT: ${Math.round(negRatio * 100)}% negative in last ${analyzed.length} reviews`65 );66 await configRef.set(67 { last_alert_at: admin.firestore.FieldValue.serverTimestamp() },68 { merge: true }69 );70 }71 }72 }7374 return null;75 } catch (err) {76 console.error('Sentiment analysis error:', err.message);77 return null;78 }79 });Common mistakes
Why it's a problem: Using only star rating and ignoring text comment sentiment
How to avoid: Store and analyze both. Show star rating for quick trend, but surface low-magnitude high-score anomalies (e.g., sarcastic 5-star reviews) using the NLP score.
Why it's a problem: Calling the Natural Language API directly from the Flutter client
How to avoid: Always call the NLP API from a Cloud Function. The Cloud Function has the key stored in firebase functions:config, which is never exposed to clients.
Why it's a problem: Not handling empty or very short feedback text
How to avoid: Check text length before calling the API. If the comment is fewer than 3 words, set sentiment_label to 'insufficient_data' and skip the API call.
Why it's a problem: Building the admin dashboard with a large unindexed Firestore query
How to avoid: Create composite indexes in the Firebase console for the fields you filter and sort on (sentiment_label + analyzed_at). Use pagination with a 50-item limit per page.
Best practices
- Analyze sentiment asynchronously via Cloud Function trigger — never block the user's form submission waiting for NLP results
- Store raw score and magnitude alongside the label so you can adjust thresholds later without re-analyzing
- Segment sentiment by user cohort, feature area, or app version to identify which parts of your product drive negative feedback
- Use magnitude to filter out noise — a score of –0.1 with magnitude 0.2 is nearly neutral, not a real complaint
- Combine sentiment with session data (screens visited before submitting) to correlate UX pain points with negative text
- Review and re-label a sample of 50 results manually each month to verify the API's accuracy for your domain
- Set budget alerts on the Google Cloud Natural Language API — costs are $1 per 1,000 calls beyond the free tier
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a FlutterFlow app with Firebase. I want a Cloud Function (Node.js) that triggers on Firestore onCreate for the 'feedback' collection, calls Google Cloud Natural Language API analyzeSentiment on the document's 'comment' field, and writes back sentiment_score, sentiment_magnitude, and sentiment_label ('positive', 'negative', 'neutral') to the document. Also add logic to check if more than 30% of the last 50 analyzed documents are negative and log a warning if so.
In FlutterFlow, I have a feedback page with a TextField and a Submit button. Describe step by step how to set up the Action Flow on the Submit button to create a Firestore document in the 'feedback' collection with the comment text, user ID from Authenticated User, and a server timestamp — without including any sentiment fields (those are added by a Cloud Function after creation).
Frequently asked questions
How accurate is Google Cloud Natural Language API for sentiment analysis?
It's highly accurate for standard English text — typically 80-90% agreement with human raters on clear positive/negative content. Accuracy drops for short texts (under 10 words), industry-specific jargon, or sarcasm. Review a sample of your own data to confirm accuracy for your use case.
What does 'magnitude' mean and when should I use it?
Magnitude measures how strongly sentiment is expressed, regardless of direction. Score of –0.9 with magnitude 3.0 means strongly and confidently negative. Score of –0.3 with magnitude 0.2 means slightly negative but the text is mostly factual. Use magnitude to filter out weak signals — only alert on high magnitude negative feedback.
Can I use OpenAI instead of Google Cloud NLP for sentiment?
Yes. Send the feedback text to GPT-4 with a prompt like 'Classify this as positive, negative, or neutral and give a confidence score 0-1'. OpenAI gives more nuanced output but costs more per call and has higher latency. Google NLP is faster and cheaper for bulk sentiment classification.
Does this work with non-English feedback?
Google Cloud Natural Language API supports 700+ languages for sentiment analysis. Pass the language code in the document object (e.g., language: 'es' for Spanish) or let the API auto-detect it. Auto-detection is reliable for texts over 20 words.
How do I display a sentiment chart in FlutterFlow?
FlutterFlow has a built-in Chart widget. Add a Backend Query at the page level loading feedback documents grouped by day. Use a Bar Chart with the date on the X axis and average sentiment_score on the Y axis. For the color fill, set positive values to green and negative to red using conditional chart styling.
What does it cost to analyze 1,000 feedback submissions per month?
Google Cloud Natural Language API has a free tier of 5,000 units per month. Beyond that, sentiment analysis is $1 per 1,000 records (each record up to 1,000 characters). 1,000 submissions per month falls within the free tier. 10,000 submissions would cost about $5/month.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation