Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Set Up a Custom Metrics Tracking System in FlutterFlow

Build a business KPI tracking system in FlutterFlow for metrics like revenue, churn rate, and active users. You will create a metrics Firestore collection with current value, previous value, and targets, a metric_history subcollection for trend data, KPI cards with trend arrows and target progress bars, sparkline Custom Widgets showing recent history, a manual update form for metrics not connected to APIs, and a Cloud Function that checks thresholds and sends alert notifications when metrics cross critical boundaries.

What you'll learn

  • How to design a Firestore metrics data model with history tracking and target values
  • How to build KPI cards that show current value, trend direction, and progress toward a target
  • How to create sparkline Custom Widgets using fl_chart to visualize recent metric history
  • How to set up threshold-based alert notifications via Cloud Functions
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner12 min read30-40 minFlutterFlow Pro+ (Custom Widgets required for sparklines)March 2026RapidDev Engineering Team
TL;DR

Build a business KPI tracking system in FlutterFlow for metrics like revenue, churn rate, and active users. You will create a metrics Firestore collection with current value, previous value, and targets, a metric_history subcollection for trend data, KPI cards with trend arrows and target progress bars, sparkline Custom Widgets showing recent history, a manual update form for metrics not connected to APIs, and a Cloud Function that checks thresholds and sends alert notifications when metrics cross critical boundaries.

Track business KPIs with trend indicators, sparklines, and threshold alerts

This tutorial builds a business metrics tracking system for non-technical founders who need to monitor key performance indicators like monthly revenue, churn rate, NPS score, and active users. Unlike app analytics that track user behavior events, this system tracks business-level KPIs that may come from manual entry, external APIs, or Firestore aggregation queries. Each metric displays its current value, comparison to the previous period with a trend arrow, progress toward a defined target, and a sparkline showing recent history. Cloud Functions monitor metrics against thresholds and send push notifications when a metric crosses a critical boundary.

Prerequisites

  • A FlutterFlow project with Firebase/Firestore connected
  • Firebase Blaze plan for Cloud Functions
  • FlutterFlow Pro plan for Custom Widgets
  • Basic understanding of Firestore documents and Backend Queries

Step-by-step guide

1

Design the metrics and metric_history Firestore collections

Create a collection called metrics. Each document represents one business KPI. Fields: name (String, e.g., 'Monthly Revenue'), type (String enum: counter, gauge, percentage, currency), currentValue (Double), previousValue (Double), target (Double), unit (String, e.g., '$', '%', 'users'), category (String, e.g., 'Financial', 'Growth', 'Engagement'), lastUpdated (Timestamp), thresholdHigh (Double, optional), thresholdLow (Double, optional). Create a subcollection called metric_history under each metric document with fields: value (Double), timestamp (Timestamp). Add 5-6 sample metrics: Monthly Revenue ($45,000 target $50,000), Active Users (1,250 target 2,000), Churn Rate (4.2% threshold 5%), NPS Score (72 target 80), Weekly Signups (89 target 100), Support Tickets Open (23 threshold 30). For each metric, add 12-16 history documents representing the past 4 months of weekly snapshots.

Expected result: The metrics collection contains 5-6 sample KPIs with current and target values. Each metric has a metric_history subcollection with weekly historical data points.

2

Build the KPI metric card Component with trend arrow and target progress bar

Create a Component called MetricCard with Parameters: metricName (String), currentValue (Double), previousValue (Double), target (Double), unit (String), type (String). Layout: Container with white background, 16px padding, 12px borderRadius, and a subtle shadow. Inside, a Column with crossAxisAlignment start. First child: a Text widget showing metricName in bodyMedium with secondary color. Second child: a Row with the formatted current value in headlineMedium bold (use a Custom Function formatByType that formats currency with '$' prefix and commas, percentages with '%' suffix, and plain numbers with K/M suffixes) and a trend indicator — an Icon (Icons.trending_up in green or Icons.trending_down in red) based on whether currentValue is greater than previousValue, plus a Text showing the percentage change. Third child: a LinearProgressIndicator showing progress toward the target. Set value to (currentValue / target).clamp(0.0, 1.0) using a Custom Function. Set the color to green if above 80% of target, amber if 50-80%, red if below 50%. Fourth child: a Text showing 'Target: {formatted target}' in caption style.

custom_functions.dart
1// Custom Function: formatByType
2// Return Type: String
3// Parameters: value (Double), type (String), unit (String)
4
5String formatByType(double value, String type, String unit) {
6 switch (type) {
7 case 'currency':
8 if (value >= 1000000) return '$unit${(value / 1000000).toStringAsFixed(1)}M';
9 if (value >= 1000) return '$unit${(value / 1000).toStringAsFixed(1)}K';
10 return '$unit${value.toStringAsFixed(0)}';
11 case 'percentage':
12 return '${value.toStringAsFixed(1)}$unit';
13 default:
14 if (value >= 1000) return '${(value / 1000).toStringAsFixed(1)}K';
15 return value.toStringAsFixed(0);
16 }
17}
18
19// Custom Function: calcProgress
20// Return Type: Double
21// Parameters: current (Double), target (Double)
22
23double calcProgress(double current, double target) {
24 if (target == 0) return 0.0;
25 return (current / target).clamp(0.0, 1.0);
26}

Expected result: A MetricCard Component displays the metric name, formatted value with trend arrow, and a color-coded progress bar toward the target.

3

Create a sparkline Custom Widget for metric history visualization

Add fl_chart to your project dependencies (fl_chart: ^0.68.0 in Custom Code settings). Create a Custom Widget called SparklineChart with parameters: dataPoints (List<JSON>, each with value as Double), lineColor (Color, default blue), height (Double, default 40). The widget renders a compact, minimal line chart with no axes, no grid, and no labels — just the trend line. Configure LineChartData with gridData show false, titlesData all hidden, borderData show false, and lineTouchData enabled false. Map dataPoints to FlSpot using the list index as x and the value as y. Set isCurved true, barWidth 2, dotData show false, and belowBarData with a subtle gradient fill. Wrap everything in a SizedBox with the height parameter. On the MetricCard Component, add the SparklineChart below the progress bar. Query the metric_history subcollection ordered by timestamp descending, limit 12, and pass the results as dataPoints. Set lineColor to match the trend direction: green if the latest value is above the earliest, red if below.

sparkline_chart.dart
1// Custom Widget: SparklineChart
2import 'package:fl_chart/fl_chart.dart';
3
4class SparklineChart extends StatelessWidget {
5 final List<dynamic> dataPoints;
6 final Color lineColor;
7 final double height;
8
9 const SparklineChart({
10 super.key,
11 required this.dataPoints,
12 this.lineColor = const Color(0xFF2196F3),
13 this.height = 40,
14 });
15
16 @override
17 Widget build(BuildContext context) {
18 final spots = dataPoints.asMap().entries.map((e) {
19 return FlSpot(
20 e.key.toDouble(),
21 (e.value['value'] as num).toDouble(),
22 );
23 }).toList();
24
25 return SizedBox(
26 height: height,
27 child: LineChart(
28 LineChartData(
29 gridData: const FlGridData(show: false),
30 titlesData: const FlTitlesData(show: false),
31 borderData: FlBorderData(show: false),
32 lineTouchData: const LineTouchData(enabled: false),
33 lineBarsData: [
34 LineChartBarData(
35 spots: spots,
36 isCurved: true,
37 color: lineColor,
38 barWidth: 2,
39 dotData: const FlDotData(show: false),
40 belowBarData: BarAreaData(
41 show: true,
42 color: lineColor.withOpacity(0.1),
43 ),
44 ),
45 ],
46 ),
47 ),
48 );
49 }
50}

Expected result: Each MetricCard shows a compact sparkline below the progress bar, visualizing the metric's trend over recent history without taking up much vertical space.

4

Build the metrics dashboard page with category filtering

Create a page called MetricsDashboard. Add a Backend Query at page level that fetches all documents from the metrics collection ordered by category ascending. Below the AppBar, add a ChoiceChips widget with options: All, Financial, Growth, Engagement. Bind the selected value to a Page State variable selectedCategory (String, default 'All'). Below the ChoiceChips, add a GridView with crossAxisCount 2, spacing 12, and childAspectRatio 0.85. Generate children from the Backend Query results filtered by selectedCategory (or show all if 'All' is selected). Each child is a MetricCard Component with parameters bound to the document fields. Add a FloatingActionButton with an Icons.edit icon. On tap, navigate to a MetricUpdatePage where admins can select a metric from a DropDown, enter a new value in a TextField, and tap Update. The Update action writes the new value to the metric document (updating currentValue, setting previousValue to the old currentValue, and updating lastUpdated) and also adds a document to the metric_history subcollection.

Expected result: The dashboard shows all business KPIs in a filterable grid. Tapping the edit button navigates to a form where admins can manually update metric values.

5

Set up threshold alerts via Cloud Function notifications

Deploy a Cloud Function called checkMetricThresholds triggered by Firestore document writes on the metrics collection (functions.firestore.document('metrics/{metricId}').onWrite). When a metric document is updated, the function reads the new currentValue and compares it against thresholdHigh and thresholdLow. If currentValue exceeds thresholdHigh or drops below thresholdLow, the function sends a push notification to admin users via Firebase Cloud Messaging. The notification title is 'Metric Alert: {metricName}' and the body describes the threshold breach. In FlutterFlow, configure push notifications in the Settings panel. Create an admin_users collection or use a Firestore field isAdmin on user documents to determine who receives alerts. Additionally, log each alert to a metric_alerts collection with metricId, alertType (high or low), triggeredValue, threshold, and timestamp for audit history.

Expected result: When a metric value crosses a defined threshold, admin users receive a push notification alerting them to the breach. All alerts are logged in the metric_alerts collection.

6

Add a weekly digest summary with period-over-period comparison

Deploy a scheduled Cloud Function called weeklyDigest that runs every Monday at 8 AM via functions.pubsub.schedule. The function reads all metrics documents, fetches the metric_history entry from 7 days ago for each metric, calculates week-over-week change, and compiles a summary. Format the summary as an HTML email using a template: metric name, current value, previous week value, change percentage with green/red color coding, and target progress. Send the email via a configured email service (SendGrid, Mailgun, or Firebase Extensions email). In FlutterFlow, add a DigestPreview page that shows the same weekly summary in-app. Use a ListView of MetricSummaryRow Components, each showing the metric name, current vs previous value, change arrow, and a mini progress bar. This page queries the same data the Cloud Function uses, giving admins an in-app preview of what the email contains.

Expected result: Admin users receive a weekly email digest every Monday summarizing all KPI changes. An in-app preview page shows the same weekly comparison data.

Complete working example

Metrics Tracking System Architecture
1Firestore Data Model:
2 metrics/{auto-id}
3 name: String (Monthly Revenue)
4 type: String (counter | gauge | percentage | currency)
5 currentValue: Double (45000)
6 previousValue: Double (42000)
7 target: Double (50000)
8 unit: String ($ | % | users)
9 category: String (Financial | Growth | Engagement)
10 lastUpdated: Timestamp
11 thresholdHigh: Double (optional)
12 thresholdLow: Double (optional)
13 metric_history/{auto-id}
14 value: Double
15 timestamp: Timestamp
16 metric_alerts/{auto-id}
17 metricId: String
18 alertType: String (high | low)
19 triggeredValue: Double
20 threshold: Double
21 timestamp: Timestamp
22
23Cloud Functions:
24 checkMetricThresholds onWrite metrics/{id}, send FCM if threshold crossed
25 weeklyDigest every Monday 8 AM, email summary of all KPIs
26
27Custom Functions:
28 formatByType(value, type, unit) formatted string
29 calcProgress(current, target) double 0.0-1.0
30 calcPercentChange(current, previous) string percentage
31
32Custom Widget:
33 SparklineChart fl_chart LineChart, no axes, compact 40px height
34
35Page: MetricsDashboard
36 AppBar (title: Business Metrics)
37 ChoiceChips [All | Financial | Growth | Engagement]
38 On Changed Update Page State: selectedCategory
39 GridView (crossAxisCount: 2, aspect: 0.85)
40 MetricCard Component (repeated, filtered by category)
41 Text (metricName, bodyMedium)
42 Row: formatted value (headlineMedium) + trend Icon + % change
43 LinearProgressIndicator (currentValue / target, color-coded)
44 Text (Target: formatted target)
45 SparklineChart (metric_history, 12 points, height: 40)
46 FloatingActionButton Navigate to MetricUpdatePage
47 Backend Query: metrics, orderBy category ASC
48
49Page: MetricUpdatePage
50 DropDown (select metric)
51 TextField (new value)
52 Button (Update write currentValue, previousValue, metric_history)
53
54Page: DigestPreview
55 ListView of MetricSummaryRow Components
56 Row: name + current + previous + change % + mini progress bar

Common mistakes

Why it's a problem: Showing only the current metric value without historical context

How to avoid: Always show three pieces of context alongside the current value: previous period comparison with a trend arrow, a sparkline of recent history, and progress toward the defined target.

Why it's a problem: Storing metric history in the same document as the current value

How to avoid: Use a metric_history subcollection under each metric document. Each history entry is its own lightweight document. Query with limit and orderBy to fetch only the data points you need for sparklines.

Why it's a problem: Not handling the division-by-zero case in percentage change calculations

How to avoid: In your calcPercentChange Custom Function, check if previousValue equals zero before dividing. Return '0.0' or 'New' as a string when there is no previous value to compare against.

Best practices

  • Always display current value, trend direction, target progress, and sparkline history together on each metric card for full context
  • Use a metric_history subcollection instead of an array field to avoid Firestore document size limits
  • Color-code progress bars by percentage of target achieved: green above 80%, amber 50-80%, red below 50%
  • Set meaningful thresholds for automated alerts — only alert on metrics that require immediate action to avoid notification fatigue
  • Use the formatByType Custom Function to handle currency, percentage, and plain number formatting consistently across all cards
  • Add pull-to-refresh on the dashboard so admins can manually refresh after updating a metric
  • Log all threshold alerts to a metric_alerts collection for audit history and trend analysis of alert frequency
  • Start with manual metric updates and add API-based automation incrementally as data sources are identified

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Design a Firestore data model for a business KPI tracking system. I need a metrics collection with fields for name, type (counter/gauge/percentage/currency), currentValue, previousValue, target, unit, category, and thresholds. Include a metric_history subcollection for trend data. Write Dart functions for formatting values by type (currency with $ and commas, percentages with %, numbers with K/M) and calculating progress toward a target as a 0-1 double.

FlutterFlow Prompt

Create a business metrics dashboard page with a ChoiceChips filter for categories (All, Financial, Growth, Engagement) and a 2-column GridView of metric cards. Each card should display the metric name, a large formatted value, a trend arrow showing up or down versus the previous period, a color-coded progress bar toward a target value, and a small sparkline chart showing recent history. Add a FloatingActionButton that navigates to a metric update form.

Frequently asked questions

How is this different from the analytics platform tutorial?

The analytics platform tutorial tracks user behavior events (page views, clicks, signups) with funnels and cohorts. This metrics tracking system tracks business-level KPIs like revenue, churn rate, and NPS that may come from manual entry, external APIs, or aggregated Firestore queries. Think of analytics as what users do, and metrics as how the business performs.

Can metrics update automatically from external APIs?

Yes. Deploy a scheduled Cloud Function that calls external APIs (Stripe for revenue, your auth system for active users, a support tool for ticket counts) and updates the corresponding metric document in Firestore. Start with manual updates to validate the dashboard, then automate one metric at a time.

How many data points should the sparkline show?

12-16 data points work well for a compact sparkline at 40px height. For weekly snapshots, this covers 3-4 months. For daily snapshots, 14 data points covers two weeks. Query metric_history ordered by timestamp descending with a limit of 12-16 to keep the widget lightweight.

What happens if a metric does not have a target value?

Set the target to zero or null and use Conditional Visibility to hide the progress bar on the MetricCard Component when target is zero. The card still shows the current value, trend arrow, and sparkline. Not every metric needs a target — some like churn rate are better tracked with thresholds than targets.

How do I add a new metric to the tracking system?

Add a new document to the metrics collection with the metric name, type, initial currentValue, target, unit, and category. The dashboard GridView automatically picks it up on the next query. No code changes or redeployment needed — this is the advantage of a data-driven approach where the UI renders whatever metrics exist in Firestore.

Can RapidDev help connect metrics to live data sources?

Yes. Connecting business metrics to live data from Stripe, Google Analytics, CRM systems, and internal databases often requires custom Cloud Functions, API authentication, data transformation, and error handling. RapidDev can build the integration layer that keeps your FlutterFlow metrics dashboard updated automatically from all your business tools.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.