# How to Create a Custom Sales Widget for Your FlutterFlow App

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 30-40 min
- Compatibility: FlutterFlow Pro+ (Custom Widget for chart, Cloud Function for aggregation)
- Last updated: March 2026

## TL;DR

Build a sales dashboard Component with KPI cards for revenue, order count, and conversion rate displayed in a 2-column GridView. Add a sparkline trend chart using fl_chart LineChart in a Custom Widget. Show a top-5 products ListView ranked by revenue. Pull all data from a pre-aggregated sales_daily Firestore collection populated by a Cloud Function, and filter by date range using Today, This Week, and This Month buttons.

## Building a Sales Dashboard with KPI Cards and Trend Charts in FlutterFlow

E-commerce and CRM apps need at-a-glance sales performance views. This tutorial builds a dashboard Component with key metric cards, a revenue trend sparkline, and a top products ranking — all powered by pre-aggregated Firestore data to keep page loads fast.

## Before you start

- A FlutterFlow project on the Pro plan (Custom Widget required for charts)
- Firestore configured with an orders collection containing order data
- Firebase Blaze plan for Cloud Function deployment
- fl_chart package added to Pubspec Dependencies

## Step-by-step guide

### 1. Create the sales_daily Firestore collection and Cloud Function aggregator

Create a sales_daily collection in Firestore. Each document represents one day: date (Timestamp), revenue (double, total sales), orderCount (int), conversionRate (double, 0.0-1.0), topProducts (Array of Map: { name: String, revenue: double, quantity: int }). Deploy a Cloud Function triggered daily via Cloud Scheduler that queries the orders collection for the previous day, sums revenue, counts orders, calculates conversion (orders / unique visitors from an analytics source), and writes the summary document. This pre-aggregation avoids expensive real-time queries on the raw orders collection.

**Expected result:** Firestore has a sales_daily collection with one document per day containing pre-computed sales metrics.

### 2. Build KPI metric cards in a 2-column GridView with percentage change indicators

On the dashboard page, add a GridView with crossAxisCount: 2, childAspectRatio: 1.6, crossAxisSpacing: 12, mainAxisSpacing: 12. Add three child Components (MetricCard) with parameters: label (String), value (String), changePercent (double), isPositive (Boolean). Each MetricCard is a Container with borderRadius: 12, elevation: 2, padding: 16. Inside: Column with Text label (bodySmall, grey), Text value (headlineMedium, bold), and a Row with Icon (arrow_upward green or arrow_downward red based on isPositive) + Text changePercent (bodySmall, green or red).

**Expected result:** Three KPI cards display in a 2-column grid showing revenue, order count, and conversion rate with up/down change indicators.

### 3. Create a SparklineChart Custom Widget using fl_chart LineChart in minimal mode

Add fl_chart: ^0.65.0 to Pubspec Dependencies. Create a Custom Widget named SparklineChart with parameters: dataPoints (List<double>), color (Color). In the build method, return a SizedBox (height: 80) containing a LineChart. Configure LineChartData with one LineChartBarData: spots mapped from dataPoints (index as x, value as y), isCurved: true, barWidth: 2, color: widget.color, dotData: FlDotData(show: false), belowBarData: BarAreaData(show: true, color: widget.color.withOpacity(0.1)). Hide all axes, grid, border, and titles for a clean sparkline look.

**Expected result:** A compact sparkline chart renders revenue trends as a smooth curved line with a subtle gradient fill area.

### 4. Add a top-5 products ListView ranked by revenue from sales_daily data

Below the sparkline, add a Text 'Top Products' (titleMedium, bold) and a ListView (shrinkWrap: true, physics: NeverScrollable) with 5 children. Each item is a Row: rank number Text (bodyLarge, bold, primary color), SizedBox(width: 12), Expanded Column (product name Text bodyMedium, quantity Text bodySmall grey), and trailing revenue Text (bodyLarge, bold). Bind the data from the topProducts array of the current day's sales_daily document. Sort by revenue descending.

**Expected result:** A ranked list shows the top 5 products by revenue for the selected date range, each with name, quantity sold, and revenue.

### 5. Add date range filter buttons for Today, This Week, and This Month

At the top of the dashboard, add a Row with three ChoiceChip-style buttons: Today, This Week, This Month. Create a Page State dateRange (String, default 'today'). On button tap, update dateRange. The Backend Query on sales_daily uses Conditional Filters: 'today' filters date == today, 'week' filters date >= 7 days ago, 'month' filters date >= 30 days ago. For multi-day ranges, the KPI values display aggregated sums (total revenue, total orders, average conversion). The sparkline dataPoints list contains one value per day in the range.

**Expected result:** Switching between Today, This Week, and This Month updates all KPI cards, the sparkline chart, and the top products list.

## Complete code example

File: `SparklineChart Custom Widget`

```dart
// Custom Widget: SparklineChart
// Pubspec: fl_chart: ^0.65.0

import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';

class SparklineChart extends StatelessWidget {
  final double width;
  final double height;
  final List<double> dataPoints;
  final Color color;

  const SparklineChart({
    Key? key,
    required this.width,
    this.height = 80,
    required this.dataPoints,
    this.color = Colors.blue,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    if (dataPoints.isEmpty) {
      return SizedBox(width: width, height: height);
    }

    final spots = dataPoints
        .asMap()
        .entries
        .map((e) => FlSpot(e.key.toDouble(), e.value))
        .toList();

    return SizedBox(
      width: width,
      height: height,
      child: LineChart(
        LineChartData(
          gridData: const FlGridData(show: false),
          titlesData: const FlTitlesData(show: false),
          borderData: FlBorderData(show: false),
          lineTouchData: LineTouchData(
            touchTooltipData: LineTouchTooltipData(
              getTooltipItems: (spots) => spots.map((s) =>
                LineTooltipItem(
                  '\$${s.y.toStringAsFixed(0)}',
                  const TextStyle(color: Colors.white, fontSize: 12),
                ),
              ).toList(),
            ),
          ),
          lineBarsData: [
            LineChartBarData(
              spots: spots,
              isCurved: true,
              color: color,
              barWidth: 2,
              isStrokeCapRound: true,
              dotData: const FlDotData(show: false),
              belowBarData: BarAreaData(
                show: true,
                color: color.withOpacity(0.1),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
```

## Common mistakes

- **Querying the raw orders collection for real-time aggregation on every dashboard load** — Scanning all orders is expensive (Firestore reads) and slow — a store with 10,000 orders causes multi-second load times and high billing. Fix: Pre-aggregate daily summaries into a sales_daily collection using a Cloud Function. The dashboard reads one summary document instead of thousands of orders.
- **Using fl_chart without hiding axes, grid, and titles for the sparkline** — Default fl_chart settings show full axis labels and gridlines, which look cluttered in a small 80px sparkline space. Fix: Set gridData: FlGridData(show: false), titlesData: FlTitlesData(show: false), borderData: FlBorderData(show: false) for a clean minimal sparkline.
- **Displaying conversion rate as a raw decimal like 0.032 instead of a percentage** — Non-technical users do not intuitively understand 0.032 as 3.2%. The metric looks meaninglessly small. Fix: Multiply the stored decimal by 100 and format with one decimal place: '${(conversionRate * 100).toStringAsFixed(1)}%' displays as '3.2%'.

## Best practices

- Pre-aggregate sales data with a scheduled Cloud Function to keep dashboard loads under 1 second
- Store topProducts as an array of maps inside the daily summary to avoid extra collection queries
- Show percentage change compared to the previous period (yesterday, last week) for context on each KPI
- Use green for positive change and red for negative change — universal visual language for financial data
- Keep the sparkline height to 60-100 pixels so it stays compact while still showing the trend shape
- Add touch tooltips on the sparkline so users can tap a point to see the exact revenue value for that day
- Default to Today view for the fastest initial load — single document read

## Frequently asked questions

### How do I calculate conversion rate if I do not track unique visitors?

Use a proxy metric like orders divided by app sessions. Alternatively, skip conversion rate and replace that KPI card with average order value (total revenue / order count).

### Can I use fl_chart for bar charts or pie charts on the dashboard?

Yes. fl_chart supports LineChart, BarChart, PieChart, RadarChart, and ScatterChart. Create separate Custom Widgets for each chart type using the same package.

### How often should the Cloud Function run to update sales_daily?

Run it once daily after midnight (e.g., 1:00 AM) to aggregate the previous day's data. For near-real-time today data, run it every hour and overwrite today's document.

### What if the sparkline has only one data point for the Today view?

A single point cannot render a line. Show a Text value instead of the sparkline when dataPoints.length < 2, or include the previous day as a second point for comparison.

### Can I export the dashboard data as a CSV?

Yes. Create a Custom Action that queries sales_daily for the selected range, formats each document as a CSV row, and uses the url_launcher or share_plus package to share the file.

### Can RapidDev help build a full analytics and reporting platform?

Yes. RapidDev can implement multi-metric dashboards, automated report generation, email report scheduling, user segmentation analytics, and custom chart visualizations.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-custom-sales-widget-for-your-flutterflow-app
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-custom-sales-widget-for-your-flutterflow-app
