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

How to Build a Personalized Fitness Plan Generator in FlutterFlow

Create an AI-powered fitness planner by collecting user stats and goals in an assessment form, sending that data to an AI API via a Cloud Function with a personal trainer system prompt, and displaying the returned plan in a TabBar with one tab per day. Add workout logging and a regeneration button for progressive overload. Always include a medical disclaimer.

What you'll learn

  • Build a multi-field assessment form to capture height, weight, goals, equipment, and available days
  • Call an AI API from a Firebase Cloud Function with a structured personal trainer system prompt
  • Display the generated plan in a TabBar where each tab is one training day
  • Save workout logs to Firestore and trigger plan regeneration for progressive overload
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner10 min read45-60 minFlutterFlow Free+ (Cloud Functions require Firebase Blaze plan)March 2026RapidDev Engineering Team
TL;DR

Create an AI-powered fitness planner by collecting user stats and goals in an assessment form, sending that data to an AI API via a Cloud Function with a personal trainer system prompt, and displaying the returned plan in a TabBar with one tab per day. Add workout logging and a regeneration button for progressive overload. Always include a medical disclaimer.

What You Are Building and Why

A generic workout plan app is easy to build. A personalized one that adapts to each user's body, goals, and available equipment is genuinely useful — and a strong differentiator in the fitness app market. FlutterFlow lets you build the entire UI visually, while a Firebase Cloud Function handles the AI API call so your API key never touches the client. The result is a polished app that feels custom-built for each user, with workout logs that feed back into the next plan generation cycle to keep the training progressive. This tutorial builds the full loop: assess → generate → log → regenerate.

Prerequisites

  • A FlutterFlow project connected to Firebase (Blaze plan for Cloud Functions)
  • An AI API key (OpenAI GPT-4o or Anthropic Claude) stored as a Firebase Function environment secret
  • Firestore collections: users, fitness_plans, and workout_logs
  • Basic knowledge of FlutterFlow forms and Page State variables

Step-by-step guide

1

Build the fitness assessment form

Create a new page called FitnessAssessmentPage. Add a ScrollView containing a Column with the following input widgets: a Slider for age (16–75), two TextFields for height (cm) and weight (kg), a DropdownButton for primary goal (options: Lose Weight, Build Muscle, Improve Endurance, Maintain Fitness), a Multi-select chip widget listing available equipment (Bodyweight Only, Dumbbells, Barbell, Resistance Bands, Full Gym), and a ToggleButtons row for days per week (3, 4, 5, or 6). Bind each widget to a Page State variable of the appropriate type. Add a prominent medical disclaimer Text widget at the bottom of the form in red: 'Consult your doctor before starting any new exercise program, especially if you have a medical condition, injury, or are new to exercise.' Add a Checkbox that must be ticked to enable the 'Generate My Plan' button.

Expected result: A complete scrollable assessment form with all fields, the medical disclaimer, the consent checkbox, and a disabled 'Generate My Plan' button that enables only when the checkbox is ticked.

2

Set up the Cloud Function with AI API call

In your Firebase project, create a Cloud Function called generateFitnessPlan. It accepts a JSON body with fields: age, heightCm, weightKg, goal, equipment (array), daysPerWeek, and optionally previousPlanSummary and recentLogs for progressive regeneration. Build a detailed system prompt that positions the AI as a certified personal trainer. Construct a user message summarising the assessment data. Call your AI API (OpenAI or Anthropic) with the system prompt and user message, requesting a structured JSON response with an array of day objects, each containing dayName, focus, and an exercises array with name, sets, reps, rest, and notes. Return the parsed JSON to the Flutter client. Store your API key using Firebase Functions config or Secret Manager — never hardcode it.

functions/index.js
1// functions/index.js
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4const axios = require('axios');
5admin.initializeApp();
6
7exports.generateFitnessPlan = functions.https.onCall(async (data, context) => {
8 if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');
9
10 const { age, heightCm, weightKg, goal, equipment, daysPerWeek, previousPlanSummary } = data;
11 const apiKey = process.env.OPENAI_API_KEY;
12
13 const systemPrompt = `You are a certified personal trainer with 10 years of experience.
14Create safe, progressive workout plans tailored to the individual.
15Always prioritize proper form and injury prevention.
16Return ONLY valid JSON matching this schema:
17{"days": [{"dayName": string, "focus": string, "exercises": [{"name": string, "sets": number, "reps": string, "rest": string, "notes": string}]}]}`;
18
19 const userMessage = `Create a ${daysPerWeek}-day/week workout plan for:
20- Age: ${age}, Height: ${heightCm}cm, Weight: ${weightKg}kg
21- Goal: ${goal}
22- Available equipment: ${equipment.join(', ')}
23${previousPlanSummary ? '- Previous plan: ' + previousPlanSummary + ' (increase intensity by 5-10%)' : ''}`;
24
25 const response = await axios.post('https://api.openai.com/v1/chat/completions', {
26 model: 'gpt-4o',
27 messages: [{ role: 'system', content: systemPrompt }, { role: 'user', content: userMessage }],
28 response_format: { type: 'json_object' },
29 }, { headers: { Authorization: `Bearer ${apiKey}` } });
30
31 const plan = JSON.parse(response.data.choices[0].message.content);
32 return plan;
33});

Expected result: Deploying the function succeeds. Calling it from the Firebase console returns a valid JSON fitness plan with the correct number of day objects.

3

Call the Cloud Function from FlutterFlow and store the plan

Create a Custom Action called callGenerateFitnessPlan. It reads all Page State variables from the assessment form and calls FirebaseFunctions.instance.httpsCallable('generateFitnessPlan') with them as a map. On success, save the returned plan JSON to a new Firestore document in the fitness_plans collection with the current user's UID, a createdAt timestamp, and a planVersion integer starting at 1. Also save the plan JSON to an App State variable called currentPlan so you can display it immediately without a Firestore read. Navigate to the PlanViewPage. Wire this action to the 'Generate My Plan' button. Show a full-screen loading overlay with an animated logo while the AI generates the plan — this typically takes 3–8 seconds.

Expected result: Tapping 'Generate My Plan' shows a loading screen, then navigates to PlanViewPage with the generated plan displayed.

4

Display the plan in a day-by-day TabBar

Create PlanViewPage. Retrieve the currentPlan from App State. The plan's days array length determines the number of tabs — use a Custom Widget or FlutterFlow's Tab Bar widget with dynamic tab count. Each tab label is the dayName field (e.g., 'Monday - Push', 'Wednesday - Pull'). Inside each tab, add a Text widget for the focus description, then a ListView of exercise cards. Each card shows the exercise name in a bold Text, and a Row with chips for sets x reps, rest time, and a small info icon that opens a Popup showing the notes field. Add a 'Mark All Complete' button at the bottom of each tab that logs the day's workout to Firestore.

Expected result: PlanViewPage shows a TabBar with one tab per training day. Each tab displays the exercises for that day in a scrollable card list.

5

Implement workout logging and progressive overload regeneration

When the user taps 'Mark All Complete' on a day tab, create a Firestore document in the workout_logs collection with fields: userId, planId, dayName, completedAt, and exercisesCompleted (the full exercise list). After logging, check if all days in the current plan have been completed within the last 7 days. If so, show a Congratulations Dialog with two buttons: 'Keep This Plan' and 'Generate Progressive Plan'. The 'Generate Progressive Plan' button calls callGenerateFitnessPlan again but this time passes previousPlanSummary — a string summarising the completed plan's exercises and volumes. The Cloud Function uses this to increase intensity by 5–10% in the new plan. Increment planVersion in Firestore so users can browse their plan history.

Expected result: Completing all workout days unlocks a 'Generate Progressive Plan' button. The new plan is visibly harder (more sets, heavier weights, reduced rest times) than the previous one.

Complete working example

callGenerateFitnessPlan.dart
1import 'package:cloud_functions/cloud_functions.dart';
2import 'package:cloud_firestore/cloud_firestore.dart';
3import 'package:firebase_auth/firebase_auth.dart';
4
5Future<Map<String, dynamic>?> callGenerateFitnessPlan({
6 required int age,
7 required double heightCm,
8 required double weightKg,
9 required String goal,
10 required List<String> equipment,
11 required int daysPerWeek,
12 String? previousPlanSummary,
13}) async {
14 final user = FirebaseAuth.instance.currentUser;
15 if (user == null) return null;
16
17 // Call the Cloud Function
18 final callable = FirebaseFunctions.instance.httpsCallable(
19 'generateFitnessPlan',
20 options: HttpsCallableOptions(timeout: const Duration(seconds: 60)),
21 );
22
23 final result = await callable.call({
24 'age': age,
25 'heightCm': heightCm,
26 'weightKg': weightKg,
27 'goal': goal,
28 'equipment': equipment,
29 'daysPerWeek': daysPerWeek,
30 if (previousPlanSummary != null) 'previousPlanSummary': previousPlanSummary,
31 });
32
33 final planData = Map<String, dynamic>.from(result.data as Map);
34
35 // Persist to Firestore
36 final planRef = await FirebaseFirestore.instance
37 .collection('fitness_plans')
38 .add({
39 'userId': user.uid,
40 'plan': planData,
41 'goal': goal,
42 'daysPerWeek': daysPerWeek,
43 'createdAt': FieldValue.serverTimestamp(),
44 'planVersion': 1,
45 'completedDays': [],
46 });
47
48 // Return plan with its Firestore ID
49 planData['planId'] = planRef.id;
50 return planData;
51}
52
53// Log a completed workout day
54Future<void> logWorkoutDay({
55 required String planId,
56 required String dayName,
57 required List<dynamic> exercises,
58}) async {
59 final user = FirebaseAuth.instance.currentUser;
60 if (user == null) return;
61
62 await FirebaseFirestore.instance.collection('workout_logs').add({
63 'userId': user.uid,
64 'planId': planId,
65 'dayName': dayName,
66 'exercisesCompleted': exercises,
67 'completedAt': FieldValue.serverTimestamp(),
68 });
69
70 // Mark day complete on the plan document
71 await FirebaseFirestore.instance
72 .collection('fitness_plans')
73 .doc(planId)
74 .update({
75 'completedDays': FieldValue.arrayUnion([dayName]),
76 });
77}

Common mistakes when building a Personalized Fitness Plan Generator in FlutterFlow

Why it's a problem: Generating a fitness plan with no medical disclaimer or consent gate

How to avoid: Add a visible medical disclaimer and a mandatory checkbox consent gate before the user can generate a plan. Log that consent with a timestamp to Firestore alongside the plan document.

Why it's a problem: Calling the AI API directly from the Flutter client instead of via a Cloud Function

How to avoid: Always proxy AI API calls through a server-side Cloud Function. Store the API key in Firebase Secret Manager or Functions environment config, never in client-side code or Firestore.

Why it's a problem: Not requesting JSON format from the AI API

How to avoid: Always pass response_format: { type: 'json_object' } for OpenAI, or include explicit JSON schema in your system prompt for other providers. Add a try-catch around JSON.parse() and return a helpful error message if parsing fails.

Best practices

  • Always include a medical disclaimer and require checkbox consent before generating any fitness plan
  • Store AI API keys exclusively in server-side environment variables, never in Flutter code or Firestore
  • Set a Cloud Function timeout of at least 60 seconds — AI generation can occasionally take 20–30 seconds
  • Archive all previous plan versions in Firestore so users can compare their progression over time
  • Include exercise notes explaining correct form — reducing injury risk improves retention
  • Validate the AI response schema before rendering — use a fallback message if a field is missing
  • Add a difficulty rating widget on each exercise so users can give feedback for the next regeneration
  • Cache the current plan locally in App State to avoid reloading from Firestore every time the user opens the plan

Still stuck?

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

ChatGPT Prompt

I am building a fitness plan generator in FlutterFlow. Write a Firebase Cloud Function in Node.js that accepts user stats (age, weight, height, goal, equipment, days per week) and calls the OpenAI GPT-4o API with a personal trainer system prompt. Return a structured JSON plan with one object per training day, each containing exercises with sets, reps, rest, and form notes.

FlutterFlow Prompt

In my FlutterFlow project, add a Custom Action called callGenerateFitnessPlan that takes age (int), heightCm (double), weightKg (double), goal (String), equipment (List<String>), and daysPerWeek (int) as parameters, calls a Firebase HTTPS Callable Function named generateFitnessPlan, saves the result to Firestore under fitness_plans, and returns the plan as a JSON map.

Frequently asked questions

Which AI API works best for generating fitness plans in FlutterFlow?

OpenAI's GPT-4o is the most reliable for structured JSON output because it supports the response_format parameter natively. Anthropic Claude 3.5 Sonnet is an excellent alternative and often produces more nuanced exercise progressions. Both work well in a Firebase Cloud Function proxy pattern.

Can I generate the plan without a Cloud Function by calling the AI API from Flutter directly?

Technically yes, but you must never do this in production. Calling an AI API from Flutter code means your API key is embedded in the app binary and can be stolen. Always use a server-side proxy such as a Firebase Cloud Function.

How do I handle the 3-8 second AI generation delay without losing users?

Show a full-screen loading overlay with an animated progress indicator and rotating motivational text. Users tolerate longer waits for personalised content, but they need visual feedback that something is happening. A 'Your plan is being built' message with an animation keeps engagement high.

What happens if the AI returns a plan that is unsafe for a user's medical condition?

Your app cannot medically screen users, so the medical disclaimer and consent checkbox are essential. You should also include low-intensity modification notes in the system prompt and recommend consulting a doctor. Consider adding a 'I have an injury or medical condition' checkbox that adjusts the prompt to request a gentler, rehabilitation-focused plan.

How do I implement progressive overload automatically?

After the user completes the full plan cycle, pass a previousPlanSummary string to the Cloud Function containing the exercises, sets, and reps from the last plan. Include an instruction in the system prompt such as 'increase volume by 5-10% from the previous plan'. The AI will generate slightly more demanding exercises, replicating what a real trainer would do.

Can I add video demonstrations for each exercise?

Yes. The simplest approach is to maintain a Firestore collection of exercises with a videoUrl field. When rendering each exercise card, look up the exercise name in that collection and display a video thumbnail. Tapping the thumbnail opens an in-app video player. You can pre-populate this collection with links to public YouTube exercise demonstration videos.

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.