NourishAI documentation
Run it, point it at your own accounts, ship it under your own brand. Written against version 1.1.0 of the download you get on CodeCanyon.
- Version v1.1.0
- Updated Jul 2026
- Platform Flutter
- Stack Flutter · AI
On this page
Introduction
A camera-first AI food scanner, barcode + Open Food Facts search, diary, diet plans, fasting, recipes, meal planner, and trackers — wrapped in a clean Riverpod architecture.
NourishAI is an AI calorie & nutrition tracker built as one Flutter app plus a single Firebase Cloud Function for AI. It is single-user personal nutrition tracking — not multi-user, not a marketplace — for iOS and Android.
The differentiators:
- Functional AI food scanner — real estimates from a photo or a text description, powered by dual providers (OpenAI + Gemini) switchable via Remote Config. All AI runs server-side, so API keys never ship in the app.
- Keyless food data via Open Food Facts — zero ongoing API cost for you, plus a bundled seed database.
- Responsible-use guardrails — safe calorie floors & weight-change pace caps, neutral BMI context, a persistent “estimates only” disclaimer, and an always-available eating-disorder support link (never premium-gated). Reviewers and stores favor these.
- Offline-first diary — Drift (SQLite) on-device cache; Firestore is the cloud store.
What’s included
- Full Flutter source (
lib/) + the Firebase Cloud Function source (functions/, TypeScript). - Drift (SQLite) offline cache for the diary, Open Food Facts barcode lookups, and recent foods; the full Firestore data model + strict per-uid security rules.
- The Ocean & Mint design system (Material 3
ColorScheme, Plus Jakarta Sans scale, 22px shapes) — light, dark and system. - The
analyzeFoodCloud Function (dual OpenAI/Gemini, server-side daily rate limiting with a premium bypass). - RevenueCat subscription stack (paywall + restore) and AdMob (banner + frequency-capped interstitial), with the full free/premium matrix enforced.
- Local-notification reminders (meal / water / fasting / weigh-in).
- Seed data — 370+ foods, 35+ recipes, 5 diet plans — and a one-tap demo data seeder.
SETUP.mdin the zip root — the full setup + reskin reference, right next to the code, plusCHANGELOG.md.- This documentation. 6 months email support, lifetime updates.
Features
AI food scanner (hero)
Photo mode (camera/gallery → analyzeFood → GPT-4o Vision or Gemini) and describe mode (free text). Results are editable with a low-confidence flag; the free tier is capped at 3 scans/day (server-enforced); failures fall back to barcode / search / manual entry.
Food logging
Barcode scanner (mobile_scanner → Open Food Facts), food search (Open Food Facts + bundled seed DB) with a per-100g / per-serving toggle, custom foods & recipes, favorites + recents, and the offline-first diary (calorie ring, macro bars, meal sections, water widget, day navigation).
Trackers
Weight & BMI (shown with healthy-range context, not a verdict) with an fl_chart trend, a fasting timer (16:8 / 18:6 / 20:4 + custom, with history), and activity logging (MET-based estimate) plus a device step counter.
Content
Five diet plans (activating one adjusts your macro targets), a recipe library with detail + “log this recipe”, and a weekly meal planner with daily calorie rollups.
Insights and streaks
A premium Insights screen over 7 / 30 / 90-day windows: average calories against target, a per-day calorie chart with a target line, average macro split, logging consistency, weight change, and current/longest logging streaks (with a streak chip on the diary header). The free tier gets the 7-day window. Copy is deliberately descriptive — averages and trends, never verdicts or an adherence grade.
Monetization
RevenueCat subscriptions (paywall, restore) plus AdMob on the free tier — a banner, and interstitials that are frequency-capped and only ever shown after a completed log (never in the scanner flow).
Every free-tier limit lives in one place — FreeTier in lib/core/constants/app_config.dart — so you can retune the whole matrix without hunting through the UI. The AI scan cap is the deliberate exception: it is enforced server-side.
Responsible-use guardrails
Targets are computed with Mifflin–St Jeor and clamped by sex-based calorie floors and a safe weekly pace cap. BMI uses neutral bands. A persistent “estimates only — consult a professional” disclaimer appears throughout, and the National Alliance for Eating Disorders support link is always available and never premium-gated.
Settings and polish
Goals/targets editor (recomputes targets), units (metric/imperial), theme (light/dark/system), configurable reminders, account deletion.
Backend (one Cloud Function, asia-southeast1)
analyzeFood runs auth → daily rate-limit/quota → provider → JSON-validate. The active provider and free cap are read from Remote Config server-side; AI keys are Functions secrets.
Architecture
nourish-ai/
lib/
app/ MaterialApp, GoRouter, theme (Ocean & Mint), app shell
core/ constants, db (Drift), network, enums, utils
features/<area>/{data,domain,presentation}
shared/ reusable widgets
functions/ Firebase Cloud Function (Node 20 + TypeScript) — analyzeFood
assets/seed/ foods.json, recipes.json
firebase.json firestore.rules firestore.indexes.jsonflutter_riverpod ^3.3.1) — hand-written providers (no riverpod codegen)go_router ^17.3.0)drift ^2.33.0)diopurchases_flutter) · AdMob (google_mobile_ads)Offline-first diary via Drift; other collections (custom foods, recipes, weights, fasts, meal plans, favorites) are Firestore-direct.
Firebase project setup
This is the buyer’s main task.
- 01Create a Firebase project. Enable Auth (Email/Password + Anonymous; Google/Apple optional), Firestore, Remote Config, and Functions (the Blaze plan is required for Functions).
- 02
Register an Android app (
com.devsnack.nourishaior your own id) and an iOS app, then runflutterfire configureto generatelib/firebase_options.dartand dropgoogle-services.json/GoogleService-Info.plist.NoteThese three files are gitignored — you keep your own. The repo ships*.exampletemplates only. - 03Deploy security rules:
firebase deploy --only firestore:rules. - 04Set the two Function secrets and deploy the function (see Cloud Function and secrets).
Data model (Firestore, all under the signed-in user): users/{uid} (profile, targets, units), diary/{date}, foods, recipes, weights, fasts, mealPlans, favorites, and usage/{date} (AI scan counters). firestore.rules enforces request.auth.uid == uid; no composite index is needed.
Cloud Function and secrets
There is one function, analyzeFood (region asia-southeast1). Set the two secrets, then deploy:
firebase functions:secrets:set OPENAI_API_KEY
firebase functions:secrets:set GEMINI_API_KEY
cd functions && npm install
firebase deploy --only functionsfunctions/src/index.ts — set both (use a dummy for the unused one) or make one optional before deploying.functions/src/index.ts (setGlobalOptions) and the client FirebaseFunctions.instanceFor(region:) in lib/features/ai_scanner/data/ai_scanner_repository.dart — they must match.AI provider and Remote Config
OpenAI (gpt-4o, vision + text) vs Gemini (gemini-1.5-flash). Keys live only in Functions secrets. The active provider and the free daily cap are read from Remote Config, server-side, in functions/src/config.ts:
openai | geminiThe quota is stored at users/{uid}/usage/{yyyy-MM-dd}. Security guarantee: no AI keys are in the Flutter app — every model call goes through analyzeFood.
ai_provider is read only server-side — the client never learns or chooses the provider, which is what keeps the keys out of the app. The client reads ads_enabled and (for wording only) free_daily_scan_cap. The cap is still enforced in the Function, because a limit enforced in the client could simply be edited out.Monetization (RevenueCat and AdMob)
RevenueCat
RevenueCat is client-SDK only (no server webhook). Paste your per-platform public SDK keys into AppConfig.revenueCatApiKeyAndroid / revenueCatApiKeyIos, and create the premium entitlement (identifier must be exactly premium) with your products. Use RevenueCat’s Test Store to test without store setup. Until keys are set, the paywall shows a “not configured” notice.
AdMob
For AdMob: put your banner / interstitial unit ids in AppConfig (Google test ids by default) and your AdMob app id in AndroidManifest.xml and Info.plist (GADApplicationIdentifier). Ads show on the free tier only, and the ads_enabled Remote Config flag can switch them off everywhere without an app update.
FreeTier.logsPerInterstitial (default: one ad per 8 logs). Raising the frequency is the fastest route to one-star reviews and store trouble — change it deliberately.Suggested pricing
Branding and customization
- App name + bundle id —
pubspec.yaml,android/app/build.gradle.kts(applicationId) +AndroidManifest.xml(android:label),ios/Runner/Info.plist(CFBundleDisplayName) + the Xcode bundle id. - Theme — all colors / type / shapes live in
lib/app/theme/. Editapp_colors.dartonce; widgets pull from the theme (no hex at call sites). Font is Plus Jakarta Sans viagoogle_fontsinapp_typography.dart. - Strings / limits / support links —
lib/core/constants/app_config.dart. Free-tier limits live in theFreeTierclass in that same file. - Food & recipe database — edit
assets/seed/foods.json/recipes.json(no code).
SETUP.md in the zip root is the step-by-step version of this section — colors, fonts, app name, bundle ids and launcher icons, plus a pre-release checklist.Build and release
# after Drift/freezed/json model changes
dart run build_runner build --delete-conflicting-outputs
flutter build appbundle --release # Android
flutter build ipa --release # iOS
cd functions && firebase deploy --only functionsApp signing
- Android: generate an upload keystore →
android/key.properties(gitignored) → add areleasesigningConfig and pointbuildTypes.releaseat it. Register the keystore SHA-1/256 in Firebase for Google sign-in on release. - iOS: Apple Developer → App Store Connect record → Xcode Runner → Signing & Capabilities (Team + Sign in with Apple) →
flutter build ipa --release. - Never commit
key.properties,*.jks/*.keystore,.p12, or provisioning profiles.
flutter run --release works out of the box — replace it with your release keystore before publishing.Production checklist
- 01Branding: app name + bundle id across the 4 native files; launcher icon.
- 02Theme: edit
lib/app/theme/app_colors.dart(light + dark). - 03Firebase:
flutterfire configure; enable Auth / Firestore / Remote Config / Functions (Blaze); social providers + SHA / iOS URL scheme / Apple capability. - 04
firebase deploy --only firestore:rules. - 05Set the 2 Function secrets →
firebase deploy --only functions. - 06Publish Remote Config
ai_provider/free_daily_scan_cap/ads_enabled. - 07RevenueCat: keys in
AppConfig+premiumentitlement + products. - 08AdMob: real unit ids in
AppConfig+ real app id inInfo.plist/ manifest (replace Google test ids). - 09Build releases + upload to Play Console / App Store Connect.
Changelog
Every version published so far. Updates are free for the life of the item.
Version 1.1.0 · Jul 2026Insights, streaks and full premium gating
- New: Insights screen (premium) — 7 / 30 / 90-day windows with average calories vs. target, a per-day calorie chart with a target line, average macro split, logging consistency, weight change and logging streaks. Free tier gets the 7-day window.
- New: logging streaks — a streak chip on the diary header that opens Insights.
- New: AdMob interstitials — frequency-capped (1 per 8 logs by default), shown only after a completed log, never in the scanner flow.
- New: client-side Remote Config —
ads_enabledswitches all ads off remotely without an app update;free_daily_scan_capdrives the scanner’s copy. - New:
SETUP.mdin the zip root — full setup + reskin guide with a pre-release checklist. - Free/premium gating completed: diet plans (1 free) and recipes (6 free) are now gated on both list and detail screens; the insights window is gated. All limits are centralised in
FreeTierinapp_config.dart. - Hitting the daily AI scan cap now offers an upgrade path alongside the existing manual-entry fallback.
- Seed data grown to 376 foods and 37 recipes (from 132 / 22).
- Fix: diary edits made offline could be stranded on-device — failed syncs were queued but never retried. The queue now drains at startup, on sign-in and on app resume.
- Test suite expanded from 3 to 53 tests, including the responsible-use guardrail math (calorie floors, safe-rate caps, BMI framing).
Version 1.0.0 · Jun 2026Initial release
- AI food scanner, barcode + Open Food Facts search, offline-first diary, trackers (weight / fasting / activity / water), diet plans / recipes / meal planner, reminders, RevenueCat + AdMob, responsible-use guardrails, seed data + demo seeder.
Support and licensing
Email or WhatsApp — 6 months of support bundled.
What support covers
- Six months of support from purchase, extendable at checkout
- Support covers bugs in the template and questions about how it is put together
- It does not cover custom feature work, third-party API changes or store review outcomes
- The Regular licence covers one free end product. Charging users for the app itself needs the Extended licence
Social sign-in (Google and Apple)
The Dart side is built (
AuthRepository.signInWithGoogle/signInWithApple, surfaced bySocialAuthButtons; the Apple button is iOS-only). Each buyer wires their own providers:flutterfire configure, then paste the web OAuth client id (client_type 3ingoogle-services.json) intoAppConfig.googleServerClientId— google_sign_in 7.x needs it for the ID token.REVERSED_CLIENT_IDfromGoogleService-Info.plistas a URL scheme inInfo.plist.