Add machine learning predictive analytics to your FlutterFlow app by extracting behavioral features from Firestore events, calling an ML model endpoint (BigQuery ML or Cloud AI Platform) via a Cloud Function, and storing churn scores and purchase probabilities back in Firestore user documents. Trigger automated re-engagement flows when scores cross thresholds. Never train a churn model on only churned users — you need both churned and retained users.
Predictive Analytics Without a Data Science Team
Machine learning for predictive analytics sounds daunting, but BigQuery ML lets you train and deploy a logistic regression or gradient boosted trees model using SQL syntax — no Python or ML frameworks required. The data comes from behavioral events you are already logging in Firestore (or can start logging today): session count, screens visited, feature usage, and last active date. A Cloud Function extracts these features, calls the BigQuery ML prediction endpoint, and writes the score back to Firestore. FlutterFlow reads the score from the user document to personalize the UI — showing win-back offers to high-churn-risk users or upsell prompts to high-purchase-probability users. The whole pipeline runs on Google Cloud with no external ML infrastructure.
Prerequisites
- FlutterFlow project with Firebase Firestore and user behavioral event logging
- Google Cloud project with BigQuery enabled (linked to your Firebase project)
- A minimum of 500 labeled training examples (churned and retained users) for meaningful model accuracy
- FlutterFlow Pro plan for Cloud Functions
Step-by-step guide
Define and log the behavioral features you will use for prediction
Define and log the behavioral features you will use for prediction
Machine learning models are only as good as their input features. Before thinking about models, decide what user behavior predicts churn or purchase in your app. Common high-signal features: `daysSinceLastActive` (most predictive for churn), `totalSessionsLast30Days`, `featureUsageCount` (how many distinct features used), `profileCompletionPercent`, `notificationOptIn` (boolean), `purchaseCount`, `averageSessionDurationSeconds`. Ensure these are being logged to Firestore. If they are not, add On Page Load tracking (see the navigation patterns tutorial) and aggregate them daily into a `userStats` document per user. The `userStats` collection should have one document per user, keyed by UID, with all computed features as numeric fields.
Expected result: A `userStats` collection exists in Firestore with one document per user containing numeric feature fields updated daily.
Export Firestore features to BigQuery for model training
Export Firestore features to BigQuery for model training
Firebase has a native BigQuery export integration. In the Firebase console, go to Project Settings > Integrations > BigQuery > Link. This exports Firebase Analytics events to BigQuery automatically. For your Firestore `userStats` features, set up a scheduled Cloud Function that exports the collection to a BigQuery table using the BigQuery client library. Create a BigQuery table named `user_features` with columns matching your `userStats` fields plus a `churned` column (1 if the user has not been active in 30 days, 0 if active). This labeled dataset is what you will train the model on.
1const { onSchedule } = require('firebase-functions/v2/scheduler');2const { initializeApp } = require('firebase-admin/app');3const { getFirestore } = require('firebase-admin/firestore');4const { BigQuery } = require('@google-cloud/bigquery');56initializeApp();78exports.exportUserStatsToBigQuery = onSchedule('every 24 hours', async () => {9 const db = getFirestore();10 const bq = new BigQuery();11 const dataset = bq.dataset('analytics');12 const table = dataset.table('user_features');1314 const snapshot = await db.collection('userStats').get();15 const rows = [];16 const now = Date.now();1718 snapshot.forEach((doc) => {19 const d = doc.data();20 const lastActive = d.lastActiveAt?.toDate()?.getTime() || 0;21 const daysSinceLastActive = Math.floor((now - lastActive) / 86400000);22 rows.push({23 userId: doc.id,24 daysSinceLastActive,25 totalSessionsLast30Days: d.totalSessionsLast30Days || 0,26 featureUsageCount: d.featureUsageCount || 0,27 purchaseCount: d.purchaseCount || 0,28 profileCompletionPercent: d.profileCompletionPercent || 0,29 churned: daysSinceLastActive > 30 ? 1 : 0,30 exportedAt: new Date().toISOString(),31 });32 });3334 if (rows.length > 0) {35 await table.insert(rows);36 }37 console.log(`Exported ${rows.length} user stats to BigQuery`);38});Expected result: A BigQuery table `analytics.user_features` is populated with user feature rows daily.
Train a churn prediction model in BigQuery ML
Train a churn prediction model in BigQuery ML
In the BigQuery console, run the CREATE MODEL SQL statement to train a logistic regression classifier. Use the `user_features` table with `churned` as the label column. BigQuery ML handles feature scaling and training automatically. After training, evaluate the model using the ML.EVALUATE function — look for an AUC-ROC above 0.75 as a baseline. If accuracy is low, check your feature quality and ensure both churned and retained users are represented in roughly equal proportions in the training data. Once the model is trained, it stays in BigQuery and you call it via SQL from your Cloud Function.
1-- Run in BigQuery console2CREATE OR REPLACE MODEL `your_project.analytics.churn_model`3OPTIONS(4 model_type = 'logistic_reg',5 input_label_cols = ['churned'],6 auto_class_weights = TRUE7) AS8SELECT9 daysSinceLastActive,10 totalSessionsLast30Days,11 featureUsageCount,12 purchaseCount,13 profileCompletionPercent,14 churned15FROM `your_project.analytics.user_features`16WHERE exportedAt < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY);1718-- Evaluate the model19SELECT *20FROM ML.EVALUATE(21 MODEL `your_project.analytics.churn_model`,22 (SELECT * FROM `your_project.analytics.user_features`23 WHERE exportedAt >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY))24);Expected result: The BigQuery console shows the trained model in the `analytics` dataset with AUC-ROC and accuracy metrics.
Create a Cloud Function that scores users and writes results to Firestore
Create a Cloud Function that scores users and writes results to Firestore
Create a Cloud Function named `scoreUserChurnRisk`. It runs daily via Cloud Scheduler. The function queries the current `userStats` for all active users, calls BigQuery ML's `ML.PREDICT` via a SQL query to get a churn probability for each user, and writes the score back to each user's document in Firestore as a `churnScore` (float, 0-1) and `churnScoreUpdatedAt` timestamp. Also compute a `purchaseProbability` field if you have a purchase prediction model. In FlutterFlow, read these fields from the user document to personalize the UI — show win-back offers when `churnScore > 0.7`, upsell prompts when `purchaseProbability > 0.6`.
1const { onSchedule } = require('firebase-functions/v2/scheduler');2const { getFirestore } = require('firebase-admin/firestore');3const { BigQuery } = require('@google-cloud/bigquery');45exports.scoreUserChurnRisk = onSchedule('every 24 hours', async () => {6 const db = getFirestore();7 const bq = new BigQuery();89 const query = `10 SELECT11 userId,12 predicted_churned_probs[OFFSET(1)].prob AS churnProbability13 FROM ML.PREDICT(14 MODEL \`your_project.analytics.churn_model\`,15 (16 SELECT17 userId,18 daysSinceLastActive,19 totalSessionsLast30Days,20 featureUsageCount,21 purchaseCount,22 profileCompletionPercent23 FROM \`your_project.analytics.user_features\`24 WHERE exportedAt >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)25 )26 )27 `;2829 const [rows] = await bq.query(query);30 const batch = db.batch();3132 rows.forEach((row) => {33 const ref = db.collection('users').doc(row.userId);34 batch.update(ref, {35 churnScore: row.churnProbability,36 churnScoreUpdatedAt: new Date(),37 });38 });3940 await batch.commit();41 console.log(`Scored ${rows.length} users`);42});Expected result: The `users` Firestore collection shows `churnScore` and `churnScoreUpdatedAt` fields updated daily for all active users.
Trigger automated re-engagement flows based on churn score
Trigger automated re-engagement flows based on churn score
Create a Firestore onUpdate Cloud Function triggered when a user document's `churnScore` changes. If the new score exceeds 0.7 (configurable) and the previous score was below that threshold, trigger a re-engagement action: send a push notification via Firebase Cloud Messaging (or email via SendGrid), create a Firestore document in a `reEngagementQueue` collection, or update a `showWinBackOffer` boolean on the user document. In FlutterFlow, read `showWinBackOffer` on app launch and conditionally display a personalized win-back offer screen or banner. This makes the ML predictions actionable in real time rather than just interesting numbers.
Expected result: Users crossing the 0.7 churn threshold receive a re-engagement notification. The FlutterFlow app shows a win-back offer on their next session.
Complete working example
1-- Step 1: Create training dataset with churned label2CREATE OR REPLACE TABLE `your_project.analytics.training_data` AS3SELECT4 userId,5 daysSinceLastActive,6 totalSessionsLast30Days,7 featureUsageCount,8 purchaseCount,9 profileCompletionPercent,10 CASE WHEN daysSinceLastActive > 30 THEN 1 ELSE 0 END AS churned11FROM `your_project.analytics.user_features`12WHERE exportedAt < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY);1314-- Step 2: Check class balance15SELECT churned, COUNT(*) as count16FROM `your_project.analytics.training_data`17GROUP BY churned;1819-- Step 3: Train churn model20CREATE OR REPLACE MODEL `your_project.analytics.churn_model`21OPTIONS(22 model_type = 'logistic_reg',23 input_label_cols = ['churned'],24 auto_class_weights = TRUE,25 max_iterations = 5026) AS27SELECT28 daysSinceLastActive,29 totalSessionsLast30Days,30 featureUsageCount,31 purchaseCount,32 profileCompletionPercent,33 churned34FROM `your_project.analytics.training_data`;3536-- Step 4: Evaluate model performance37SELECT38 precision,39 recall,40 f1_score,41 roc_auc,42 log_loss43FROM ML.EVALUATE(44 MODEL `your_project.analytics.churn_model`,45 (46 SELECT * FROM `your_project.analytics.training_data`47 WHERE MOD(ABS(FARM_FINGERPRINT(userId)), 5) = 048 )49);5051-- Step 5: Get feature importance52SELECT *53FROM ML.FEATURE_IMPORTANCE(MODEL `your_project.analytics.churn_model`)54ORDER BY importance_weight DESC;5556-- Step 6: Predict churn probability for current users57SELECT58 userId,59 predicted_churned_probs[OFFSET(1)].prob AS churnProbability,60 CASE61 WHEN predicted_churned_probs[OFFSET(1)].prob > 0.7 THEN 'high'62 WHEN predicted_churned_probs[OFFSET(1)].prob > 0.4 THEN 'medium'63 ELSE 'low'64 END AS churnRisk65FROM ML.PREDICT(66 MODEL `your_project.analytics.churn_model`,67 (68 SELECT69 userId,70 daysSinceLastActive,71 totalSessionsLast30Days,72 featureUsageCount,73 purchaseCount,74 profileCompletionPercent75 FROM `your_project.analytics.user_features`76 WHERE exportedAt >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)77 )78)79ORDER BY churnProbability DESC;Common mistakes
Why it's a problem: Training the churn model on only churned users without including retained users
How to avoid: Export all users — active and churned — to the BigQuery training table. Use `auto_class_weights = TRUE` to handle any imbalance between the two classes. Aim for at least 200 examples of each class before training.
Why it's a problem: Running ML prediction queries client-side or exposing the BigQuery dataset to the Flutter app directly
How to avoid: Always call BigQuery from a Cloud Function. The Cloud Function uses the Firebase project's default service account, which can be granted BigQuery Data Viewer access from the Google Cloud IAM console without any credentials in the app.
Why it's a problem: Scoring all users every hour instead of daily
How to avoid: Run the scoring Cloud Function once per day via Cloud Scheduler. This matches the natural granularity of daily behavioral features and keeps BigQuery costs minimal.
Best practices
- Use `auto_class_weights = TRUE` in BigQuery ML to handle class imbalance between churned and retained users.
- Start with 5 high-signal features rather than 50 — daysSinceLastActive alone explains most churn variance.
- Evaluate model performance with AUC-ROC, not just accuracy — a model that predicts everyone as retained looks 90% accurate if 90% of users are retained.
- Store ML scores in the user's Firestore document so FlutterFlow can read them like any other field without extra API calls.
- Add a rate-limit on re-engagement triggers — never send two win-back messages within 14 days to the same user.
- Monitor model drift monthly — retrain the model every 4 weeks as user behavior patterns change.
- Log when a user who received a re-engagement message returns to the app, so you can measure the campaign's conversion rate.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a BigQuery table `user_features` with columns: userId, daysSinceLastActive, totalSessionsLast30Days, featureUsageCount, purchaseCount, profileCompletionPercent, churned. I want to train a logistic regression model using BigQuery ML and then run daily predictions for all active users. Write the SQL to: (1) train the model with auto class weights, (2) evaluate it using a holdout set, (3) query predictions with a churn probability score and risk tier (high/medium/low) for each user. Also explain what AUC-ROC means and what value I should aim for.
I have a Firebase Cloud Function that queries BigQuery ML predictions and writes a `churnScore` field back to each user's Firestore document. I want to add a Firestore onUpdate trigger that fires when `churnScore` changes and sends a push notification via Firebase Cloud Messaging if the new score is above 0.7. Write the Cloud Function for the Firestore trigger including the FCM notification and a check that re-engagement was not already sent in the last 14 days.
Frequently asked questions
How many users do I need before machine learning churn prediction is useful?
You need at least 500 users with known outcomes (churned or retained) for a statistically meaningful model. With fewer than 200 examples per class, the model will overfit and its predictions on new users will be unreliable. For early-stage apps, use simpler rules (e.g., send a re-engagement email if a user has not opened the app in 14 days) until you have enough data for ML.
What is the difference between BigQuery ML and calling OpenAI or Anthropic for predictions?
BigQuery ML trains a custom statistical model on your own user data. It learns from your specific app's behavior patterns. Large language models like GPT-4 are general-purpose text AI — they cannot predict your users' churn without being shown your historical data. For tabular predictions (numbers like session counts and days since last active), BigQuery ML or Vertex AI AutoML are the right tools, not LLMs.
How much does BigQuery ML cost to train and run predictions?
BigQuery ML training uses the same pricing as BigQuery queries — $5 per TB processed. A table with 10,000 users and 10 features is tiny (under 10MB) — training costs pennies. Daily predictions for 10,000 users also cost under $0.01. The main cost driver is the daily Firestore batch write to update user documents — at $0.18 per 100,000 writes, 10,000 users costs $0.018/day.
Can I use ML to predict what feature a user will want next, not just churn?
Yes. This is next-action prediction. Train a multi-class classification model where the label is the next feature the user engaged with (e.g., 'settings', 'upload', 'share'). Use recent navigation history as features. The model output is a probability distribution over possible next actions. Use the highest-probability prediction to surface contextual prompts or reorder navigation elements.
What if my model's churn predictions are wrong for a specific user segment?
This is called model bias or segmentation error. Investigate by breaking down model performance by user cohort (new vs. old users, paid vs. free, mobile vs. web). If accuracy is low for a specific segment, train a separate model for that segment or add segment-specific features. You can also use the ML.FEATURE_IMPORTANCE function to identify which features drive predictions for different user groups.
Do I need to retrain the model after new users join the app?
Not immediately. A model trained on existing users can score new users from day one. However, retrain monthly to incorporate the behavior patterns of newer user cohorts, which may differ from early users. If you launch a new major feature or change your onboarding, retrain immediately since user behavior will shift significantly.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation