# How to Add a Personalized Diet Planner to Your App (Copy-Paste Prompts Included)

- Tool: App Features
- Last updated: July 2026

## TL;DR

A personalized diet planner needs a health onboarding quiz, a TDEE calorie calculator, a daily meal plan grid with swap functionality, nutrition progress rings, and a grocery list generator. With FlutterFlow or Lovable you can ship a working mobile feature in 6–12 hours. Running cost is $0–29/mo for small user bases — the Spoonacular free tier covers early usage. Costs scale with recipe API calls and push notification volume.

## What a Personalized Diet Planner Feature Actually Is

A diet planner does more than list foods — it calculates how much a specific user should eat based on their body metrics and goals, then surfaces recipes that match their calorie target and dietary constraints. The core engine is the TDEE (Total Daily Energy Expenditure) calculation using the Mifflin-St Jeor formula applied to the user's weight, height, age, gender, and activity level. From that calorie target, a meal planning API (Spoonacular or Edamam) or a pre-seeded recipe database returns four daily meal slots. The real complexity is handling dietary restrictions and allergens correctly — most first builds get the happy path right but silently serve recipes containing allergens because the API parameters are misused. Push notifications for meal reminders and a grocery list that aggregates the week's ingredients are standard table stakes in 2026.

## Anatomy of the Feature

Seven components; the TDEE calculation and allergen filtering are where most first builds produce incorrect results. Be explicit about both in your prompt.

- **Onboarding quiz flow** (ui): Multi-step form wizard collecting age, weight (kg), height (cm), gender, goal (lose weight / maintain / gain muscle), dietary preference (omnivore, vegetarian, vegan, keto, paleo), and food allergies (multi-select). Flutter Stepper widget (mobile) or shadcn/ui multi-step form (web). Calculates TDEE client-side using the Mifflin-St Jeor equation on the final step and saves to user_profiles table.
- **Meal suggestion engine** (service): Spoonacular Meal Planning API — generates a daily meal plan given a calorie target and diet type. Parameters: targetCalories (TDEE), diet ('vegan', 'keto', etc.), exclude (comma-separated allergen list from user_profiles.allergies). Or Edamam Meal Planning API with equivalent parameters. For pre-seeded apps, use a Supabase recipes table with pgvector similarity search to find recipes matching the user's macro targets.
- **Daily meal plan grid** (ui): Flutter GridView or Column with 4 meal slots per day (breakfast, lunch, dinner, snack). Each slot displays the recipe name, a thumbnail image from the API response, and the calorie count. Tapping a slot expands a modal or navigates to the full recipe detail screen with ingredients and steps. A swap icon in the top-right of each card triggers an alternative recipe fetch.
- **Nutrition progress rings** (ui): fl_chart RadialBarChart (Flutter) rendering three concentric rings for protein, carbs, and fat. Each ring fills to the percentage of the daily target consumed so far. Values update in real time when a meal slot is marked as logged. The daily nutrition target (in grams) is derived from the TDEE: 30% protein, 40% carbs, 30% fat by default, or custom macro ratios from user_profiles.
- **Grocery list generator** (data): Client-side Dart or JavaScript function that iterates all 28 meal slots in the 7-day meal plan, extracts ingredient lists from each recipe (stored in meal_plans.meals JSONB), de-duplicates by ingredient name, sums quantities, and groups by food category (produce, dairy, grains, proteins, other). Output is stored in a Supabase shopping_list_items table or displayed in a stateful Flutter list.
- **Progress tracker** (data): Two Supabase tables: weight_logs (user_id, date, weight_kg) and daily_nutrition_logs (user_id, date, kcal, protein_g, carbs_g, fat_g). Weight log screen displays the last 30 entries as an fl_chart LineChart with a linear regression trend line. Daily log screen shows a summary of what the user consumed vs their targets for the current day.
- **Push notification scheduler** (service): Firebase Cloud Messaging (FCM) — free, unlimited pushes via Google's messaging layer. A Supabase Edge Function triggered by a daily pg_cron job at 7am UTC (or user's local time offset) fetches the day's meal plan for each user and sends a personalized FCM notification with breakfast and lunch names. OneSignal is a simpler alternative with a visual dashboard for scheduling notifications at configurable times per user.

## Data model

Five tables covering the user health profile, meal plans, weight logs, daily nutrition logs, and push token storage. Run this in the Supabase SQL editor — all RLS policies restrict data to the authenticated user:

```sql
create table public.user_profiles (
  user_id uuid primary key references auth.users(id) on delete cascade,
  dob date,
  weight_kg decimal(5,2),
  height_cm decimal(5,1),
  gender varchar(10),
  goal varchar(30),
  activity_level varchar(20) not null default 'moderate',
  dietary_preference jsonb not null default '[]',
  allergies jsonb not null default '[]',
  tdee_kcal int,
  unit_preference varchar(10) not null default 'metric',
  fcm_token text,
  notification_time time not null default '07:00:00',
  updated_at timestamptz not null default now()
);

create table public.meal_plans (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  week_start date not null,
  meals jsonb not null default '{}',
  generated_at timestamptz not null default now(),
  constraint meal_plans_user_week_unique unique (user_id, week_start)
);

create table public.weight_logs (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  date date not null default current_date,
  weight_kg decimal(5,2) not null,
  created_at timestamptz not null default now(),
  constraint weight_logs_user_date_unique unique (user_id, date)
);

create table public.daily_nutrition_logs (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  date date not null default current_date,
  kcal int not null default 0,
  protein_g decimal(6,2) not null default 0,
  carbs_g decimal(6,2) not null default 0,
  fat_g decimal(6,2) not null default 0,
  updated_at timestamptz not null default now(),
  constraint daily_logs_user_date_unique unique (user_id, date)
);

-- Row Level Security
alter table public.user_profiles enable row level security;
alter table public.meal_plans enable row level security;
alter table public.weight_logs enable row level security;
alter table public.daily_nutrition_logs enable row level security;

create policy "Users manage own profile"
  on public.user_profiles for all
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create policy "Users manage own meal plans"
  on public.meal_plans for all
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create policy "Users manage own weight logs"
  on public.weight_logs for all
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create policy "Users manage own nutrition logs"
  on public.daily_nutrition_logs for all
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

-- Indexes
create index weight_logs_user_date_idx on public.weight_logs (user_id, date desc);
create index daily_logs_user_date_idx on public.daily_nutrition_logs (user_id, date desc);
create index meal_plans_user_week_idx on public.meal_plans (user_id, week_start desc);
```

The meal_plans.meals JSONB column stores the full 7-day plan as a nested object: {monday: {breakfast: {id, name, image, kcal, ingredients}, lunch: {...}, dinner: {...}, snack: {...}}, tuesday: {...}, ...}. This structure lets you fetch the entire week in one query and manipulate individual slots client-side for swaps without additional round-trips.

## Build paths

### Flutterflow — fit 4/10, 1–2 days

Best path for a native mobile diet planner — FlutterFlow's Firebase integration auto-wires auth and Firestore for data persistence; fl_chart nutrition rings render natively; image_picker enables food photo logging. Choose this when the mobile experience is the product.

1. Create a FlutterFlow project with Firebase as the backend; enable Firebase Auth with email/password and set up Firestore collections mirroring the data model above
2. Build the onboarding Stepper pages (one page per quiz step); on the final step, add a custom Dart action that calculates TDEE using the Mifflin-St Jeor formula and writes the result to the user_profiles Firestore document
3. Add a custom Dart HTTP action for the Spoonacular API call — pass targetCalories, diet type from user_profiles.dietary_preference, and exclude allergens from user_profiles.allergies as separate URL parameters
4. Add fl_chart RadialBarChart as a custom widget for the nutrition progress rings — FlutterFlow's built-in charts do not support the concentric ring layout needed for macro tracking
5. In FlutterFlow → Settings → App Settings → Permissions, enable Push Notifications; configure Firebase Cloud Messaging and add the FCM token save action on app open to keep user_profiles.fcm_token current

Limitations:

- The Spoonacular or Edamam API call requires a custom Dart HTTP action — there is no built-in FlutterFlow connector for meal planning APIs
- fl_chart RadialBarChart for nutrition rings must be added as a custom widget since FlutterFlow's visual chart blocks do not support the concentric ring layout
- The meal plan grid with individually swappable slots needs custom widget code for the swap interaction and loading state per slot
- FCM push notification scheduling per user's local time requires a Cloud Function or Supabase Edge Function — not achievable via FlutterFlow's visual workflow blocks alone

### Lovable — fit 3/10, 6–10 hours

Viable as a PWA alternative when a web-first or cross-platform experience is acceptable. Supabase backend is strong; the Mifflin-St Jeor TDEE calculation and recipe API integration work well in Lovable. The nutrition ring UI and push notifications require extra follow-up prompts.

1. Create a new Lovable project with Lovable Cloud to auto-provision Supabase auth and database; paste the prompt below in Agent Mode
2. In Lovable Cloud tab → Secrets, add SPOONACULAR_API_KEY with your Spoonacular key — the meal planning API call must run through a Supabase Edge Function, not the frontend, to keep the key secret
3. After the base feature generates, prompt Lovable to add a Supabase Edge Function that calls Spoonacular with both the diet parameter and the exclude/intolerances allergen list from user_profiles
4. For push notifications, add an FCM_SERVER_KEY secret and prompt a second Edge Function that triggers daily meal reminder pushes via the FCM HTTP v1 API
5. Publish and test the onboarding quiz, TDEE calculation, and meal plan generation on the published URL — the nutrition ring chart needs testing on a real mobile browser

Starter prompt:

```
Build a personalized diet planner as a PWA using Supabase. Schema: user_profiles table (user_id pk references auth.users, dob date, weight_kg decimal(5,2), height_cm decimal(5,1), gender varchar(10), goal varchar(30), activity_level varchar(20) default 'moderate', dietary_preference jsonb default '[]', allergies jsonb default '[]', tdee_kcal int, fcm_token text, notification_time time default '07:00'); meal_plans table (id uuid pk, user_id references auth.users, week_start date, meals jsonb, constraint unique on user_id + week_start); weight_logs (id, user_id, date, weight_kg, unique user_id + date); daily_nutrition_logs (id, user_id, date, kcal, protein_g, carbs_g, fat_g, unique user_id + date). All tables: RLS restricting all operations to auth.uid() = user_id. Onboarding: multi-step form (4 steps) collecting weight, height, date of birth, gender, goal (lose weight / maintain / gain muscle), activity level (sedentary / light / moderate / active), dietary preference (checkboxes: omnivore, vegetarian, vegan, keto, paleo), and food allergies (multi-select: gluten, dairy, eggs, nuts, soy, shellfish). On submit, calculate TDEE using the Mifflin-St Jeor formula exactly as: base = (10 * weight_kg) + (6.25 * height_cm) - (5 * age_years) + 5 (male) or -161 (female); multiply by activity factor (sedentary=1.2, light=1.375, moderate=1.55, active=1.725); store the result as tdee_kcal in user_profiles. Daily plan page: show 4 meal slots (breakfast, lunch, dinner, snack). Fetch or generate this week's meal plan via a Supabase Edge Function that calls Spoonacular Meal Planning API with targetCalories=tdee_kcal, diet=dietary_preference[0] (or omit if omnivore), and exclude=allergies joined as comma-separated string. Store the full plan response in meal_plans.meals as JSONB keyed by day name. Each meal slot card shows recipe name, image, kcal. A swap button on each card fetches 3 alternative recipes from Spoonacular with the same calorie range and dietary constraints and shows them in a bottom sheet. Nutrition progress section: show protein, carbs, fat as three progress bars (shadcn/ui Progress) filled to percentage of daily target. Update when user taps 'Log meal' on a slot, which inserts/upserts the macros to daily_nutrition_logs. Grocery list page: aggregate all ingredients from meal_plans.meals for the current week; group by category (produce, dairy, grains, proteins, other); show as a checklist. Weight log page: shadcn/ui form to add today's weight; Recharts LineChart showing the last 30 entries. Handle TDEE onboarding: if user_profiles.tdee_kcal is null, redirect to the onboarding quiz on first login.
```

Limitations:

- The nutrition ring UI (concentric circular progress bars for protein/carbs/fat) requires a follow-up prompt to implement correctly — Lovable's first pass often generates three separate flat progress bars instead
- Push notifications via FCM require a Supabase Edge Function with FCM HTTP v1 API calls — this is hard to test before publishing and requires a second round of prompting
- The PWA camera for food photo logging is limited on iOS Safari compared to a native Flutter app with image_picker
- Complex JSONB meal plan manipulation (swapping one slot without regenerating the entire week) sometimes requires a corrective follow-up prompt after the initial build

### Custom — fit 5/10, 3–6 weeks

Required when the diet planner needs Apple Health or Google Health Connect sync, AI-personalized meal recommendations that adapt to user feedback, clinical-grade nutrition tracking with dietitian oversight, or barcode scanning to log packaged foods.

1. Apple Health / Google Health Connect sync: use HealthKit (iOS) or Health Connect API (Android) to read weight, steps, and active calories; feed real-time activity data into the TDEE calculation for more accurate daily targets
2. AI personalized recommendations: after a user swaps or rejects a meal, send the feedback to Claude or GPT via a Supabase Edge Function; build a preference profile in a user_meal_feedback table; use it to filter subsequent Spoonacular calls
3. Clinical nutrition tracking: add a dietitian_id foreign key to user_profiles; build a dietitian dashboard where nutritionists can view patient macro logs, override TDEE targets, and add notes; HIPAA considerations apply if storing health data in the US
4. Barcode scanning for packaged foods: integrate mobile_scanner Flutter package with Open Food Facts API lookup; show the product's nutrition facts and let users log a serving size directly to daily_nutrition_logs

Limitations:

- HealthKit and Health Connect require platform-specific Swift/Kotlin code or a React Native plugin — not achievable in a pure web or FlutterFlow build without custom code
- HIPAA compliance for clinical nutrition apps requires a Business Associate Agreement with your database and hosting provider, plus audit logging, encryption at rest, and access controls beyond standard Supabase RLS

## Gotchas

- **Recipes match the diet type but still contain allergens** — Passing only diet='vegan' to Spoonacular or Edamam returns vegan recipes — but many vegan recipes contain gluten, nuts, or soy. The diet parameter controls the overall recipe classification, not ingredient-level allergen exclusion. A user who selects 'vegan + nut allergy' receives recipes with peanuts or tree nuts if the intolerances parameter is omitted. Fix: Always pass BOTH the diet parameter AND the intolerances or exclude parameter as separate URL parameters. For Spoonacular: ?diet=vegan&intolerances=peanut,tree+nut. For Edamam: &health=vegan&health=peanut-free&health=tree-nut-free. Double-check the exact parameter names in the API docs — they differ between services and AI tools frequently confuse them.
- **TDEE calculation returns numbers 30–50% off the correct value** — A common AI bug is applying the activity factor multiplier before completing the Mifflin-St Jeor base formula, or mixing up the gender constant (+5 for male, -161 for female). The result is a TDEE that is either dramatically low (making meal plans with only 800–900 kcal) or implausibly high (over 4,000 kcal for a sedentary person). Fix: Specify the formula in the prompt step by step: Step 1 — base = (10 x weight_kg) + (6.25 x height_cm) - (5 x age_years) + 5 if male, or -161 if female. Step 2 — TDEE = base x activity_factor. Activity factors: sedentary=1.2, lightly active=1.375, moderately active=1.55, very active=1.725. Add a visible sanity check to the UI: most adults' TDEE falls between 1,400 and 3,500 kcal.
- **Grocery list shows duplicate ingredients in incompatible units** — Recipes from Spoonacular use mixed measurement systems — one recipe lists '2 cups flour', another lists '300g flour', a third '10 oz flour'. A naive grocery list aggregation adds these as three separate line items for the same ingredient, and the totals are meaningless. This appears as a real usability failure when users take the grocery list to a store. Fix: Normalize all ingredient measurements to grams and milliliters on ingest using a unit conversion map (1 cup flour = 120g, 1 oz = 28.35g, 1 tbsp = 15ml). Group by a canonical ingredient name (lowercase singular) after normalization, then sum. Display the final quantity in the most human-friendly unit for that ingredient (e.g., '480g flour' rather than '480000mg flour').
- **FlutterFlow app crashes on null meal plan slots** — When the meal_plans.meals JSONB column contains null values for days not yet planned — for example, the plan was only generated for 3 of the 7 days — Flutter tries to access nested properties on a null object. Without null safety checks throughout the Dart model, the app throws a Null check operator used on a null value error and the meal plan page crashes. Fix: Use ?? null coalescing throughout the Dart meal plan model when accessing nested JSONB properties: mealPlan['monday']?['breakfast']?['name'] ?? 'Not planned yet'. Initialize each day's four meal slots with empty default maps when generating a partial plan so the JSONB structure is always complete even for unplanned days.

## Best practices

- Cache the weekly meal plan in Supabase meal_plans table on first generation and reuse it — regenerating a fresh plan on every session burns Spoonacular API points and disorients users who remember what they planned to eat on Thursday
- Always pass both diet and intolerances as separate Spoonacular API parameters — diet='vegan' does not exclude nut allergens from vegan recipes; intolerances must be listed explicitly
- Display the TDEE calculation result with a brief plain-language explanation ('Your daily target: 2,100 kcal, based on your height, weight, and moderate activity level') — users who understand how their target was set are more likely to trust and follow it
- Normalize ingredient units to grams and milliliters before aggregating the grocery list — incompatible units produce duplicate line items that destroy the grocery list's usefulness at the store
- Store the FCM token on every app open and update it in user_profiles — FCM tokens rotate and expire, and stale tokens cause silent notification delivery failures that are hard to debug
- Show a loading skeleton on each meal slot card while the Spoonacular API call is in progress — recipe fetches take 200–800ms and a blank screen makes users think the app has crashed
- Add a visible sanity check on the TDEE output: flag values below 1,200 kcal or above 4,000 kcal with a warning message and prompt the user to review their inputs before saving
- Store weight in kilograms internally and convert to the user's preferred unit (kg or lbs) only at display time — this prevents floating-point precision loss from repeated conversion

## Frequently asked questions

### How do I calculate how many calories to show as the daily target?

Use the Mifflin-St Jeor equation: (10 x weight_kg) + (6.25 x height_cm) - (5 x age_years) + 5 for males, or -161 for females. Then multiply by an activity factor — 1.2 for sedentary, 1.375 for lightly active, 1.55 for moderately active, 1.725 for very active. The result is the TDEE (Total Daily Energy Expenditure). For weight loss, subtract 300–500 kcal. For muscle gain, add 200–300 kcal. Apply the goal adjustment after the activity multiplier, not before.

### Can the app automatically generate a grocery list from the weekly meal plan?

Yes — iterate all 28 meal slots in the 7-day plan, extract ingredient lists from each recipe, normalize units to grams and milliliters, de-duplicate by canonical ingredient name, sum quantities, and group by food category. The key complexity is unit normalization: recipes use cups, ounces, grams, and tablespoons interchangeably. A unit conversion table (1 cup flour = 120g, 1 oz = 28.35g) handles most cases. Store the output in a shopping_list_items Supabase table or display it as a stateful checklist.

### How do I handle users with multiple dietary restrictions at the same time — like vegan and nut allergy?

Use both Spoonacular parameters simultaneously: diet=vegan&intolerances=peanut,tree+nut. The diet parameter controls recipe classification (no animal products) while the intolerances parameter controls ingredient-level exclusion (no peanuts or tree nuts in any ingredient). Never assume the diet parameter handles allergen exclusion — vegan recipes regularly contain nuts, gluten, and soy. Test your API calls with a user profile that has both a diet restriction and an allergen to verify both parameters are being sent.

### Can users swap individual meals without regenerating the entire weekly plan?

Yes — store the full 7-day plan in the meal_plans.meals JSONB column keyed by day and meal slot. When the user taps Swap on a slot, call Spoonacular with the same calorie range and dietary constraints but exclude the current recipe's ID. Display 3 alternatives in a bottom sheet. When the user selects one, update only that slot in the JSONB object with an upsert — the other 27 slots remain unchanged. This saves API points and preserves the user's existing plan.

### How do I sync the app with Apple Health or Google Fit?

Apple HealthKit requires iOS-native Swift code or a React Native package like react-native-health. Google Health Connect (Android 14+) uses the Android Health Connect SDK. Neither is accessible from a web app or standard FlutterFlow build without a custom widget. The most practical approach for a FlutterFlow app is a custom Dart plugin or a dedicated React Native build. Plan for 2–3 weeks of extra development time for a properly tested HealthKit integration.

### What is the best recipe API to use for a diet planner?

Spoonacular is the most commonly used for diet planner apps — its meal planning endpoint generates a full day or week's plan from a calorie target and diet type in a single API call. Edamam has stronger dietitian-grade nutrition accuracy and is preferred for clinical or medical apps. Open Food Facts is free but covers packaged foods rather than recipes. The USDA FoodData Central is best for ingredient-level nutrition lookup rather than complete recipe suggestions.

### Can I add a food diary where users log what they actually ate versus what was planned?

Store planned meals in meal_plans.meals JSONB and logged meals separately in daily_nutrition_logs (kcal, protein_g, carbs_g, fat_g). When a user taps 'Log this meal', add the recipe's macros to the daily log via an upsert. The nutrition progress rings read from daily_nutrition_logs, not from meal_plans. This decoupling lets users log meals they cooked themselves or ate out without breaking the planned meal view.

### How do I send meal reminders via push notification at the right time of day?

Store the user's preferred notification time (e.g., 07:00) in user_profiles.notification_time alongside their timezone or UTC offset. A Supabase Edge Function triggered by pg_cron every hour fetches all users whose notification_time matches the current UTC hour (adjusted for their offset), retrieves their day's meal plan from meal_plans, and dispatches an FCM push via the Firebase HTTP v1 API. Store the FCM registration token in user_profiles.fcm_token and refresh it on every app open — tokens rotate and stale tokens cause silent delivery failures.

---

Source: https://www.rapidevelopers.com/app-features/personalized-diet-planner
© RapidDev — https://www.rapidevelopers.com/app-features/personalized-diet-planner
