Build a fitness tracker using Firestore workouts collection with a dynamic exercise form that lets users add multiple exercises per session. Display progress on a dashboard with KPI cards, a weekly BarChart Custom Widget using fl_chart, a CircularPercentIndicator for goal progress, and a streak counter. Users log sets, reps, and weight per exercise, and a Custom Function calculates calories burned from duration and intensity.
Workout logging dashboard with goals, charts, and streaks
This tutorial builds a fitness tracking app where users log completed workouts with multiple exercises per session, set weekly goals for calories or workout count, and track progress on a visual dashboard. The dashboard shows KPI cards (total workouts, calories burned this week), a weekly BarChart, a goal progress ring, and a workout streak counter. This logs COMPLETED workouts — for planning future routines, see the workout planner tutorial.
Prerequisites
- A FlutterFlow project with Firebase/Firestore connected
- Firebase Authentication enabled with users signing in
- Basic understanding of Page State and Custom Functions in FlutterFlow
- Familiarity with ListView and Container widgets
Step-by-step guide
Design the Firestore workouts and goals collections
Design the Firestore workouts and goals collections
Create a workouts collection with fields: userId (String), exercises (List of Maps — each with name String, sets Integer, reps Integer, weight Double), durationMinutes (Integer), caloriesBurned (Integer), timestamp (Timestamp), and notes (String). Create a goals collection with fields: userId (String), type (String — calories, workouts, or streakDays), targetValue (Integer), currentValue (Integer), period (String — weekly or monthly), and startDate (Timestamp). Set Firestore rules so users can only read and write their own documents. Add 3-4 test workouts with varied dates for chart testing.
Expected result: Firestore has workouts and goals collections with test data scoped to individual users.
Build the dynamic workout logging form with Add Exercise rows
Build the dynamic workout logging form with Add Exercise rows
Create a LogWorkoutPage. Add a Page State variable exerciseList of type List of JSON (initially empty). Place a Column with: a Text header 'Log Workout', a ListView bound to exerciseList where each row has four fields — TextField for exercise name, TextField (numeric) for sets, TextField (numeric) for reps, and a Slider for weight (0-300 lbs). Each row also has an IconButton (Icons.remove_circle) that removes that entry from exerciseList. Below the ListView, add an ElevatedButton 'Add Exercise' that appends a new empty JSON object {name: '', sets: 0, reps: 0, weight: 0} to exerciseList. At the bottom, add a TextField for durationMinutes and a Save Workout button whose Action Flow creates a Firestore document in workouts with all the exerciseList entries, the calculated caloriesBurned from your Custom Function, and the current timestamp.
Expected result: Users can add multiple exercise rows dynamically, fill in sets/reps/weight for each, and save the complete workout to Firestore.
Create the dashboard with KPI cards and streak counter
Create the dashboard with KPI cards and streak counter
Build a DashboardPage. At the top, add a Row of three Container KPI cards. Card 1: 'Workouts This Week' — Backend Query counting workouts where userId == currentUser AND timestamp >= startOfWeek. Card 2: 'Calories Burned' — Custom Function summing caloriesBurned from the same weekly query results. Card 3: 'Current Streak' — Custom Function that queries workouts ordered by timestamp desc, iterates backwards checking for consecutive days with at least one workout, and returns the streak count. Style each card with the value in headlineMedium bold and the label in bodySmall secondary color. Add a flame icon next to the streak count for visual emphasis.
Expected result: Dashboard shows three KPI cards with real-time weekly workout count, calories burned, and current streak number.
Add the weekly BarChart using fl_chart Custom Widget
Add the weekly BarChart using fl_chart Custom Widget
Create a Custom Widget named WeeklyBarChart that imports the fl_chart package. The widget accepts a parameter weeklyData (List of JSON with day String and value Integer). Inside the widget, build a BarChart with 7 bars (Mon-Sun), each bar's height set to the calories or workout count for that day. Style bars with rounded tops and a gradient from primary color to primaryLight. Add axis labels for days of the week along the bottom. On the DashboardPage, query workouts for the current week, group by day using a Custom Function, and pass the grouped data to the WeeklyBarChart widget. Place it in a Container with 200px height below the KPI cards.
1// Custom Widget: WeeklyBarChart (simplified)2import 'package:fl_chart/fl_chart.dart';34BarChart(5 BarChartData(6 barGroups: List.generate(7, (i) {7 final value = widget.weeklyData.length > i8 ? (widget.weeklyData[i]['value'] as num).toDouble()9 : 0.0;10 return BarChartGroupData(x: i, barRods: [11 BarChartRodData(12 toY: value,13 width: 20,14 borderRadius: BorderRadius.vertical(top: Radius.circular(6)),15 gradient: LinearGradient(16 colors: [Color(0xFF4CAF50), Color(0xFF81C784)],17 begin: Alignment.bottomCenter, end: Alignment.topCenter,18 ),19 ),20 ]);21 }),22 titlesData: FlTitlesData(23 bottomTitles: AxisTitles(sideTitles: SideTitles(24 showTitles: true,25 getTitlesWidget: (value, meta) => Text(26 ['M','T','W','T','F','S','S'][value.toInt()],27 ),28 )),29 ),30 ),31)Expected result: A bar chart with 7 bars shows daily workout activity for the current week with gradient coloring.
Display goal progress with CircularPercentIndicator
Display goal progress with CircularPercentIndicator
Below the chart, add a goal progress section. Query the user's active goal document from the goals collection. Use the CircularPercentIndicator widget (add the percent_indicator package in pubspec dependencies) with percent set to currentValue / targetValue (capped at 1.0). Set the lineWidth to 12, progressColor to green, backgroundColor to grey[200], and place a Text widget in the center showing 'currentValue / targetValue'. Below the indicator, show the goal type label (e.g., 'Weekly Calories Goal'). Add an Edit Goal button that opens a BottomSheet with a TextField for targetValue and a DropDown for goal type. On save, update the goal document. A Cloud Function triggered on workout creation should increment the matching goal's currentValue and reset goals weekly.
Expected result: A circular progress ring shows how close the user is to their weekly goal, with a center label and edit button.
Build the workout history list with detail view
Build the workout history list with detail view
Create a HistoryPage (or a tab on the dashboard) with a ListView bound to a Backend Query on workouts where userId == currentUser.uid ordered by timestamp desc, limit 20 with infinite scroll. Each list item Container shows: date (formatted timestamp), workout summary (e.g., '4 exercises, 45 min'), calories burned with a flame icon, and a right chevron. On Tap, navigate to a WorkoutDetailPage that receives the workout document reference and displays all exercises in a DataTable-style layout with columns for Exercise Name, Sets, Reps, and Weight. Add a Delete Workout IconButton in the AppBar with a confirmation dialog.
Expected result: Users can browse their workout history in a scrollable list and tap any entry to see the full exercise breakdown.
Complete working example
1Firestore Data Model:2├── workouts/{workoutId}3│ ├── userId: String4│ ├── exercises: List<Map>5│ │ └── [{name: "Bench Press", sets: 3, reps: 10, weight: 135},6│ │ {name: "Squats", sets: 4, reps: 8, weight: 185}, ...]7│ ├── durationMinutes: Integer (45)8│ ├── caloriesBurned: Integer (320)9│ ├── timestamp: Timestamp10│ └── notes: String11└── goals/{goalId}12 ├── userId: String13 ├── type: String ("calories" | "workouts" | "streakDays")14 ├── targetValue: Integer (2000)15 ├── currentValue: Integer (1450)16 ├── period: String ("weekly")17 └── startDate: Timestamp1819DashboardPage:20├── Row (3 KPI Cards)21│ ├── Container: "Workouts This Week" + count22│ ├── Container: "Calories Burned" + sum23│ └── Container: "Streak" + flame icon + days24├── WeeklyBarChart Custom Widget (fl_chart)25│ └── 7 bars (Mon-Sun), height = daily calories26├── CircularPercentIndicator27│ ├── percent: currentValue / targetValue28│ ├── center: "1450 / 2000"29│ └── label: "Weekly Calories Goal"30└── Button: "Log Workout" → Navigate to LogWorkoutPage3132LogWorkoutPage:33├── Text: "Log Workout"34├── ListView (bound to Page State exerciseList)35│ └── Row per exercise36│ ├── TextField (name)37│ ├── TextField (sets, numeric)38│ ├── TextField (reps, numeric)39│ ├── Slider (weight: 0-300)40│ └── IconButton (remove row)41├── Button: "+ Add Exercise" → append to exerciseList42├── TextField (durationMinutes)43└── Button: "Save Workout" → Create Document + Navigate backCommon mistakes when developing a Fitness Tracking App Using FlutterFlow
Why it's a problem: Storing exercises as a flat array without structure — cannot query across workouts for specific exercises
How to avoid: If you need per-exercise queries (e.g., 'all bench press history'), use an exercises subcollection under each workout instead of an array. For simpler apps where you only view exercises within a workout, the array approach is fine.
Why it's a problem: Calculating streak by counting all workouts instead of checking consecutive days
How to avoid: Write a Custom Function that groups workouts by date (ignoring time), then iterates backwards from today counting consecutive days that have at least one workout. Stop counting when a day is missed.
Why it's a problem: Not resetting weekly goals at the start of each new week
How to avoid: Deploy a scheduled Cloud Function that runs every Monday at midnight, queries all active weekly goals, and resets currentValue to 0. Alternatively, calculate currentValue dynamically by summing this week's workouts in a Custom Function.
Best practices
- Use Page State lists for the dynamic exercise form so users can add and remove rows freely
- Calculate calories in a Custom Function rather than asking users to estimate — reduce friction
- Query only the current week's workouts for dashboard KPIs to keep reads low and response fast
- Use a scheduled Cloud Function to reset weekly goals instead of client-side date checks
- Cap the CircularPercentIndicator at 1.0 (100%) even if the user exceeds their goal
- Add infinite scroll with limit 20 on workout history to handle users with hundreds of logged workouts
- Store streak count as a denormalized field on the user document and update it via Cloud Function on each workout creation
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Design a Firestore data model for a fitness tracking app with workouts (containing a dynamic list of exercises with sets, reps, weight) and goals (weekly calorie or workout count targets). Write a Dart function that calculates the current workout streak by checking for consecutive days with at least one workout.
Create a workout logging page with a dynamic form where users can add multiple exercises. Each exercise row has fields for name, sets, reps, and weight. Include an Add Exercise button and a Save Workout button that stores everything to Firestore.
Frequently asked questions
How do I calculate calories burned for different exercise types?
Create a Custom Function that uses MET (Metabolic Equivalent of Task) values. Store a map of exercise names to MET values (e.g., running: 9.8, weightlifting: 6.0, cycling: 7.5). Calculate: calories = MET * weight_kg * duration_hours. For simplicity, use a flat rate like 6 calories per minute as a default.
Can I integrate with Apple Health or Google Fit?
FlutterFlow does not have built-in health kit integration. You need a Custom Widget or Custom Action using the health package in Dart. It reads step count, heart rate, and activity data from the device's health APIs. This requires FlutterFlow Pro plan for custom code.
How do I show exercise history for a specific movement like Bench Press?
If exercises are stored as an array in the workout doc, you need to fetch all workouts and filter client-side. For better performance on large datasets, store exercises in a subcollection so you can query across all workouts with where name == 'Bench Press'.
How does the streak counter work if the user misses one day?
The streak resets to zero when a calendar day has no workout logged. The Custom Function checks consecutive days backwards from today. If yesterday has no workout and the user has not yet logged today, the streak shows 0. Consider adding a grace period (reset only after 2 missed days) for a friendlier experience.
Can I add workout templates so users do not have to enter exercises manually each time?
Yes. Create a workout_templates collection with predefined exercise lists. On the LogWorkoutPage, add a 'Use Template' button that queries templates and populates the exerciseList Page State with the template's exercises. Users can then adjust sets, reps, and weight before saving.
Can RapidDev help build a fitness app with wearable integration and social features?
Yes. A production fitness app with Apple Health/Google Fit sync, social challenges, leaderboards, personalized workout plans, and push notification reminders requires custom native code and Cloud Functions that go beyond the visual builder. RapidDev can build the full system.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation