Feature spec
IntermediateCategory
vertical-tools
Build with AI
4–8 hours with Lovable or FlutterFlow
Custom build
2–3 weeks custom dev
Running cost
$0–$25/mo up to 1K users
Works on
Everything it takes to ship an Interactive Recipe Cook Along — parts, prompts, and real costs.
An interactive cook-along needs a full-screen step-by-step UI with large text, per-step countdown timers that fire background notifications, a WakeLock to keep the screen on, and a serving size scaler. With FlutterFlow you can ship a working native cook-along mode in 4–8 hours for $0–$25/month — all timers and voice recognition run on-device at no per-call cost. iOS background timer behavior is the main gotcha that breaks first builds.
What an Interactive Recipe Cook Along Feature Actually Is
A cook-along mode transforms a standard recipe page into a guided cooking session: the app moves through each step one at a time, shows a countdown timer for steps that require it, keeps the phone screen awake so the user is not touching the screen with floury hands, and listens for voice commands to advance steps hands-free. The feature is not just a carousel of recipe steps — it is a complete UX shift designed for a kitchen environment where the phone is propped up 60cm away. The product decisions that matter: how precisely do background timers work on iOS (they are heavily restricted), whether voice control is push-to-talk or always-on (always-on causes false triggers from kitchen sounds), and whether the cook-along works fully offline for recipe apps with a travel or off-grid audience.
What users consider table stakes in 2026
- Full-screen step-by-step cooking mode with large text (minimum 22sp) readable from counter distance of 60cm
- Per-step countdown timer with a visual ring progress indicator and an audio or vibration alert when the timer expires
- Voice navigation responding to commands like 'next step', 'go back', and 'pause timer' without requiring the user to touch the screen with messy hands
- Ingredient checklist with checkable items that persist through the session so users can track what they have already measured
- Serving size scaler that recalculates all ingredient quantities in real time when the user changes the serving count
- Keep-screen-awake mode (WakeLock) that prevents the phone from sleeping during a cooking session and releases properly when the session ends
Anatomy of the Feature
Six components. The cook mode UI and timer are generated correctly by AI tools; voice navigation and iOS background timer behavior are where first builds consistently break.
Cook mode UI
UIFlutter full-screen step card using flutter_screenutil for responsive typography that scales across phone sizes. PageView widget enables swipe-to-advance between steps. WakeLock.enable() called in initState keeps the screen on for the duration of the session.
Note: WakeLock must be released in dispose() or the screen will stay on even after the user exits. Use wakelock_plus package — it handles the lifecycle automatically.
Per-step timer
UIFlutter's Timer.periodic drives a countdown in seconds stored in Page State. A CustomPainter draws the ring progress indicator around the time display. flutter_local_notifications schedules a notification at the moment cooking starts so the alarm fires even if the app is sent to the background or killed by iOS.
Note: Do not rely on Timer.periodic alone for the alarm — iOS will kill background timers. Schedule a local notification with a fixed fire time instead.
Voice navigation
ServiceFlutter speech_to_text package listens for wake words ('next step', 'go back', 'pause timer'). Processes on-device via ML Kit Speech Recognition — no internet required and no per-call cost. Confidence threshold set to 0.85 or higher to avoid false triggers from kitchen sounds.
Note: Keep voice as a secondary control with push-to-talk as the primary. Always-on microphone listening in a noisy kitchen produces too many false positives for reliable use.
Recipe data model
DataSupabase tables: recipes (id, title, servings, image_url, prep_time_min, cook_time_min, user_id) and recipe_steps (id, recipe_id, step_number, instruction, timer_seconds, image_url). Serving scaler multiplies ingredient quantities by the ratio of new_servings to original_servings on the client — no server round-trip needed.
Note: Store ingredient quantities as numerics, not formatted strings. '1/2 cup' cannot be multiplied — store 0.5 and format on display.
Ingredient checklist
UIFlutter CheckboxListTile widgets with local state managed by flutter_riverpod or Provider. Checked state persists in SharedPreferences for session continuity if the app is backgrounded mid-recipe. State resets automatically when cook mode is launched fresh.
Note: Do not persist checkbox state to Supabase — ingredient checking is a transient session state, not permanent user data.
Offline support
BackendHive or Isar local database caches the full recipe data (steps, ingredients, images) on the first successful load. Cook mode reads from the local cache, making the feature fully functional offline. Supabase sync runs on the next network connection to pick up any recipe edits.
Note: Cache recipe images to device storage via cached_network_image with CacheManager. Without image caching, cook mode degrades to text-only when offline.
The data model
Three tables for recipes, steps, and ingredients with row-level security for draft/published state. Run in the Supabase SQL editor.
1create table public.recipes (2 id uuid primary key default gen_random_uuid(),3 user_id uuid references auth.users(id) on delete cascade not null,4 title text not null,5 servings int not null default 4,6 image_url text,7 prep_time_min int not null default 0,8 cook_time_min int not null default 0,9 is_published bool not null default false,10 created_at timestamptz not null default now()11);1213alter table public.recipes enable row level security;1415create policy "Published recipes are viewable by everyone"16 on public.recipes for select17 using (is_published = true OR auth.uid() = user_id);1819create policy "Owners can manage their recipes"20 on public.recipes for all21 using (auth.uid() = user_id);2223create table public.recipe_steps (24 id uuid primary key default gen_random_uuid(),25 recipe_id uuid references public.recipes(id) on delete cascade not null,26 step_number int not null,27 instruction text not null,28 timer_seconds int,29 image_url text,30 unique (recipe_id, step_number)31);3233alter table public.recipe_steps enable row level security;3435create policy "Steps inherit recipe visibility"36 on public.recipe_steps for select37 using (38 exists (39 select 1 from public.recipes r40 where r.id = recipe_id41 and (r.is_published = true OR auth.uid() = r.user_id)42 )43 );4445create policy "Owners can manage steps"46 on public.recipe_steps for all47 using (48 exists (49 select 1 from public.recipes r50 where r.id = recipe_id and auth.uid() = r.user_id51 )52 );5354create table public.recipe_ingredients (55 id uuid primary key default gen_random_uuid(),56 recipe_id uuid references public.recipes(id) on delete cascade not null,57 name text not null,58 quantity numeric not null,59 unit text not null,60 order_index int not null61);6263alter table public.recipe_ingredients enable row level security;6465create policy "Ingredients inherit recipe visibility"66 on public.recipe_ingredients for select67 using (68 exists (69 select 1 from public.recipes r70 where r.id = recipe_id71 and (r.is_published = true OR auth.uid() = r.user_id)72 )73 );7475create policy "Owners can manage ingredients"76 on public.recipe_ingredients for all77 using (78 exists (79 select 1 from public.recipes r80 where r.id = recipe_id and auth.uid() = r.user_id81 )82 );8384create index recipe_steps_recipe_id_idx on public.recipe_steps (recipe_id, step_number);85create index recipe_ingredients_recipe_id_idx on public.recipe_ingredients (recipe_id, order_index);Heads up: Store ingredient quantity as a numeric value (e.g. 0.5 for half a cup). Formatting as fractions ('1/2 cup') is a display concern handled on the client — you cannot multiply a string by the serving ratio.
Build it — pick your path
Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.
The native mobile path and the right choice for this feature. FlutterFlow's Widget Tree maps directly to the step card layout; Flutter handles WakeLock, ML Kit speech, background timer notifications, and offline Hive storage natively.
Step by step
- 1Create a FlutterFlow project with Supabase integration; run the SQL schema from this page in the Supabase SQL editor
- 2Build the cook mode page: add a PageView widget with one child per recipe step, bind step_number and instruction from the recipe_steps table query
- 3Add a Page State variable for the countdown timer; use a Timer.periodic Custom Action to decrement it every second and fire a flutter_local_notifications notification when it reaches zero
- 4Add a Custom Action for WakeLock using the wakelock_plus package — call enable in the page's init action and disable in the dispose action
- 5Add voice control via a Custom Action using speech_to_text; bind recognized text to a page state variable and trigger next/previous step actions conditionally
Where this path bites
- ML Kit speech recognition in a Custom Action requires the Pro plan ($70/mo) — it cannot be used on Standard
- Voice command accuracy drops in noisy kitchens; test with real background cooking noise before launching
- FlutterFlow auto-generated pubspec.yaml may include conflicting speech package versions on code export — reconcile dependency versions before the App Store build
Third-party services you'll need
All core cook-along features run on-device at zero per-call cost. Supabase is the only recurring service expense.
| Service | What it does | Free tier | Paid from |
|---|---|---|---|
| Supabase | Recipe and ingredient data storage, user auth, and recipe image storage in Supabase Storage | 2 projects, 500MB database | $25/mo Pro plan |
| ML Kit | On-device speech recognition for voice commands — no internet required, no per-call cost | Free (bundled in Flutter via Google ML Kit) | Free — on-device processing |
| flutter_local_notifications | Background timer notifications that fire even when the app is backgrounded or killed | Free — open-source package | Free |
Swipe the table sideways to see pricing.
What it costs to run
Drag through the tiers to see how your monthly bill scales with users — no surprises later.
Estimated monthly running cost
Supabase free tier covers recipe data and image storage at this scale. ML Kit runs on-device with no per-call costs. No recurring service fees.
Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.
What breaks when AI tools build this
The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.
WakeLock not releasing drains battery after cooking session ends
Symptom: WakeLock.enable() prevents the screen from sleeping but must be balanced by WakeLock.disable() when the user exits cook mode. If the widget is disposed without calling disable — because the user navigates back or the app is backgrounded — the screen lock stays active indefinitely, draining the battery for hours.
Fix: Use the wakelock_plus package instead of the raw wakelock package — it provides automatic lifecycle handling via the WidgetsBindingObserver. Call WakelockPlus.enable() in initState and WakelockPlus.disable() in dispose() wrapped in a try/finally block to ensure it runs even if an exception occurs.
Background timer fires but app is already killed on iOS
Symptom: iOS aggressively suspends and kills apps that go to the background. Flutter's Timer.periodic is not reliable after 30 seconds in background. Users who switch to another app mid-timer get no notification when their pasta water finishes boiling — the most critical failure mode for a cooking app.
Fix: Use flutter_local_notifications to schedule a notification at the exact timestamp when the timer should expire (DateTime.now().add(Duration(seconds: timerSeconds))). This schedules an OS-level notification that fires even if the app is killed. Do not rely on Timer.periodic alone for the user-facing alarm.
Voice commands trigger randomly from kitchen sounds
Symptom: The speech_to_text package keeps the microphone open continuously when voice mode is active. In a working kitchen with running water, sizzling, and background music, the recognizer picks up random audio and triggers 'next step' or 'pause timer' incorrectly — advancing through the recipe without any user intent.
Fix: Increase the confidence threshold to 0.85 or higher to filter low-confidence recognitions. Add a 500ms debounce between accepted commands. Most importantly, make voice a secondary control — show a large push-to-talk button as the primary interaction and make voice activation opt-in. This dramatically reduces false triggers in real kitchens.
FlutterFlow Custom Action for ML Kit conflicts with existing speech plugin
Symptom: FlutterFlow auto-generates a pubspec.yaml that may include an older version of the speech_to_text package alongside the ML Kit plugin you added via Custom Action. Dart pub resolves to an incompatible version, and the Flutter build fails with a dependency conflict error only discovered at the App Store submission stage.
Fix: After code export, open pubspec.yaml and manually reconcile the speech_to_text and google_mlkit_* dependency versions. Pin to the latest stable compatible versions and run flutter pub upgrade. Verify the build locally before uploading to TestFlight or Play Console.
Serving scaler shows 0.33333 recurring fractions
Symptom: Simple multiplication of ingredient quantities by a serving ratio produces floating-point fractions: 1/3 cup times 2 = 0.6666666. Displaying raw decimal fractions is confusing in a cooking context — 0.75 cups is far less useful than '3/4 cup'.
Fix: Round liquid quantities to 1 decimal place. For small quantities, convert to the nearest useful cooking fraction: 0.25 = '1/4', 0.33 = '1/3', 0.5 = '1/2', 0.75 = '3/4'. Use a small fractions lookup function rather than displaying raw decimals.
Best practices
Schedule background timer notifications using flutter_local_notifications with a fixed fire time — never rely on Timer.periodic alone for the alarm, as iOS kills background processes
Release WakeLock in the widget's dispose() method inside a try/finally block — an unreleased WakeLock drains the battery for hours after cooking ends
Use push-to-talk as the primary voice control mechanism and always-on listening as opt-in secondary — kitchen noise produces too many false triggers for always-on to be reliable
Store ingredient quantities as numeric values in the database and format to useful fractions (1/4, 1/3, 1/2) only at display time — strings cannot be multiplied by the serving ratio
Cache recipe data and images to a local Hive or Isar database on first load so cook mode works fully offline — kitchens often have poor connectivity
Use flutter_screenutil for all text and spacing so the cook mode UI is readable on both small (5-inch) and large (7-inch tablet) screens from counter distance
Reset ingredient checklist state when cook mode starts fresh, but persist it in SharedPreferences across app-background events during a session so a phone call does not lose the user's progress
Request microphone permission before the cook mode starts with a clear explanation — 'We use the microphone to respond to hands-free commands like Next step while you cook' increases permission grant rates
When You Need Custom Development
FlutterFlow handles the standard cook-along feature well. Custom development is justified for these specific requirements:
- The app needs Siri Shortcuts or Google Assistant integration so users can launch cook mode hands-free from the lock screen by saying 'Hey Siri, start cooking [recipe name]'
- Custom wake words are required (e.g. the app's brand name instead of 'next step') — PicoVoice Porcupine SDK enables custom wake words but requires a commercial license
- Integration with smart home devices (Alexa timer sync, Google Home announcements) for a premium cooking experience
- Multilingual voice commands beyond English — ML Kit supports multiple languages but mapping kitchen command vocabulary across languages requires custom training data
RapidDev builds these features for production
Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.
Frequently asked questions
Can I build a cooking app for iOS without a Mac?
You can build and design the app using FlutterFlow's web editor without a Mac. However, submitting to the iOS App Store requires a Mac with Xcode for the final build and upload — or a cloud build service like Codemagic that handles Xcode on a remote Mac. FlutterFlow's built-in iOS build exports a .ipa you can upload without a local Mac via Codemagic.
How do I keep the phone screen on during cooking?
Use the wakelock_plus Flutter package: call WakelockPlus.enable() when cook mode starts and WakelockPlus.disable() in dispose(). This works on both Android and iOS. On iOS it maps to UIApplication.shared.isIdleTimerDisabled. Always release the lock when the user exits — an unreleased WakeLock drains the battery significantly.
Does voice control work without internet?
Yes, when using ML Kit Speech Recognition via the speech_to_text Flutter package. ML Kit processes audio fully on-device using a downloaded language model. No audio is sent to a server, which means voice commands work offline and there are no per-call costs.
How do I add multiple timers for the same recipe?
Each recipe step can have its own timer_seconds value in the recipe_steps table. Show a timer UI only for steps where timer_seconds is not null. To run multiple timers simultaneously (for example, 'simmer sauce' while 'boiling pasta'), manage multiple Timer.periodic instances in a map keyed by step ID, and schedule separate flutter_local_notifications for each one's expiry time.
Can I sync recipes across devices?
Yes. Supabase handles cross-device sync automatically — when the user logs in on a new device, the app fetches their recipes from the database. The local Hive cache is device-specific and is refreshed from Supabase on each launch. Offline edits on one device are synced when the device reconnects.
How do I scale a recipe for different serving sizes?
Store ingredient quantities as numeric values (e.g. 0.5 for half a cup). When the user changes the serving count, multiply each quantity by new_servings/original_servings. Round liquids to 1 decimal place and convert small quantities to useful cooking fractions: 0.25 = '1/4 cup', 0.333 = '1/3 cup', 0.5 = '1/2 cup'. Never store quantities as formatted strings like '1/2 cup' — you cannot do math on a string.
What's the difference between a PWA recipe app and a native app?
A PWA (built with Lovable or V0) runs in the browser and has access to the Web WakeLock API on Android Chrome, but iOS Safari does not support WakeLock in PWAs and blocks background notifications. A native Flutter app (via FlutterFlow) has full access to WakeLock, flutter_local_notifications, ML Kit speech, and offline storage on both Android and iOS. For a cook-along feature where WakeLock and background timers are essential, native is the correct platform.
Can I import recipes from other apps?
Recipe import from external apps requires parsing each app's export format, which varies widely. The most portable format is JSON-LD recipe schema markup that many recipe sites embed in their HTML. A Supabase Edge Function can fetch a URL, extract the JSON-LD, and map it to your recipe_steps table. Full import from proprietary apps like Yummly or Paprika requires format-specific parsing.
Need this feature production-ready?
RapidDev builds an interactive recipe cook along into real apps — auth, database, payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.