devsnack
Documentation

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.

$18 on CodeCanyon
  • Version v1.1.0
  • Updated Jul 2026
  • Platform Flutter
  • Stack Flutter · AI
On this page
01

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 analyzeFood Cloud 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.md in the zip root — the full setup + reskin reference, right next to the code, plus CHANGELOG.md.
  • This documentation. 6 months email support, lifetime updates.
02

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).

FeatureFreePremium
AI scans
3/day
Unlimited
Barcode + search logging
Included
Included
Diet plans
1
All 5
Recipes
6
Full library
Meal planner
View only
Full editing
Insights window
7 days
30 / 90 days
Ads
Shown
Removed
Eating-disorder support link
Never gated
Included

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.

03

Architecture

nourish-ai/
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.json
Framework
Flutter 3.x · Dart 3.11
State
Riverpod 3 (flutter_riverpod ^3.3.1) — hand-written providers (no riverpod codegen)
Routing
GoRouter (go_router ^17.3.0)
Models
freezed + json_serializable
Local DB
Drift / SQLite (drift ^2.33.0)
Charts / fonts
fl_chart · google_fonts (Plus Jakarta Sans)
Food data
Open Food Facts (keyless) via dio
Camera / scan / steps
image_picker · mobile_scanner · pedometer
Auth
Firebase Auth (email · Google · Apple · guest)
Cloud data
Cloud Firestore
AI backend
Cloud Functions (Node 20 + TypeScript) · OpenAI + Gemini
Subscriptions / ads
RevenueCat (purchases_flutter) · AdMob (google_mobile_ads)
Notifications
flutter_local_notifications + timezone

Offline-first diary via Drift; other collections (custom foods, recipes, weights, fasts, meal plans, favorites) are Firestore-direct.

04

Firebase project setup

This is the buyer’s main task.

  1. 01
    Create a Firebase project. Enable Auth (Email/Password + Anonymous; Google/Apple optional), Firestore, Remote Config, and Functions (the Blaze plan is required for Functions).
  2. 02

    Register an Android app (com.devsnack.nourishai or your own id) and an iOS app, then run flutterfire configure to generate lib/firebase_options.dart and drop google-services.json / GoogleService-Info.plist.

    Note
    These three files are gitignored — you keep your own. The repo ships *.example templates only.
  3. 03
    Deploy security rules: firebase deploy --only firestore:rules.
  4. 04
    Set 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.

05

Social sign-in (Google and Apple)

The Dart side is built (AuthRepository.signInWithGoogle / signInWithApple, surfaced by SocialAuthButtons; the Apple button is iOS-only). Each buyer wires their own providers:

  • Firebase → Authentication → Sign-in method: enable Email/Password + Anonymous (guest), and Google / Apple if used.
  • Google · Android: register your app’s SHA-1/SHA-256 in Firebase, re-run flutterfire configure, then paste the web OAuth client id (client_type 3 in google-services.json) into AppConfig.googleServerClientId — google_sign_in 7.x needs it for the ID token.
  • Google · iOS: add the REVERSED_CLIENT_ID from GoogleService-Info.plist as a URL scheme in Info.plist.
  • Apple · iOS: add the Sign in with Apple capability to the Runner target in Xcode and enable the Apple provider in Firebase Auth.
06

Cloud Function and secrets

There is one function, analyzeFood (region asia-southeast1). Set the two secrets, then deploy:

terminal
firebase functions:secrets:set OPENAI_API_KEY
firebase functions:secrets:set GEMINI_API_KEY
cd functions && npm install
firebase deploy --only functions
OPENAI_API_KEY
platform.openai.com → API keys
Note
You only strictly need the key for the active provider, but both secrets are declared in functions/src/index.ts — set both (use a dummy for the unused one) or make one optional before deploying.
Warning
If you move regions, change it in both functions/src/index.ts (setGlobalOptions) and the client FirebaseFunctions.instanceFor(region:) in lib/features/ai_scanner/data/ai_scanner_repository.dart — they must match.
07

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:

Remote Config keyDefaultRead byControls
ai_provider
openai
Function
openai | gemini
free_daily_scan_cap
3
Function (enforces) + client (copy)
Free-tier scans/day (premium bypasses)
ads_enabled
true
Client
Master switch for banners and interstitials — turn all ads off without shipping an update

The 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.

Note
Which side reads what. 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.
08

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.

Warning
Interstitial pacing is set by 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

Monthly
~$4.99
Annual
~$29.99
Lifetime
~$49.99
09

Branding and customization

  • App name + bundle idpubspec.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/. Edit app_colors.dart once; widgets pull from the theme (no hex at call sites). Font is Plus Jakarta Sans via google_fonts in app_typography.dart.
  • Strings / limits / support links lib/core/constants/app_config.dart. Free-tier limits live in the FreeTier class in that same file.
  • Food & recipe database — edit assets/seed/foods.json / recipes.json (no code).
Note
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.
10

Build and release

terminal
# 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 functions

App signing

  • Android: generate an upload keystore → android/key.properties (gitignored) → add a release signingConfig and point buildTypes.release at 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.
Warning
The template currently falls back to the debug signing config so flutter run --release works out of the box — replace it with your release keystore before publishing.
11

Production checklist

  1. 01
    Branding: app name + bundle id across the 4 native files; launcher icon.
  2. 02
    Theme: edit lib/app/theme/app_colors.dart (light + dark).
  3. 03
    Firebase: flutterfire configure; enable Auth / Firestore / Remote Config / Functions (Blaze); social providers + SHA / iOS URL scheme / Apple capability.
  4. 04
    firebase deploy --only firestore:rules.
  5. 05
    Set the 2 Function secrets → firebase deploy --only functions.
  6. 06
    Publish Remote Config ai_provider / free_daily_scan_cap / ads_enabled.
  7. 07
    RevenueCat: keys in AppConfig + premium entitlement + products.
  8. 08
    AdMob: real unit ids in AppConfig + real app id in Info.plist / manifest (replace Google test ids).
  9. 09
    Build releases + upload to Play Console / App Store Connect.
12

Changelog

Every version published so far. Updates are free for the life of the item.

v1.1.0 · Jul 2026
Premium insights screen with 7/30/90-day averages, calorie chart, macro split and logging streaks. AdMob interstitials with a remote kill switch. Seed data expanded to 376 foods and 37 recipes.
v1.0.0
Initial release.

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 Configads_enabled switches all ads off remotely without an app update; free_daily_scan_cap drives the scanner’s copy.
  • New: SETUP.md in 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 FreeTier in app_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.
13

Support and licensing

Email or WhatsApp — 6 months of support bundled.

Note
Your Firebase project, OpenAI/Gemini keys, and RevenueCat + AdMob accounts are your responsibility — we provide setup walkthroughs, not accounts.

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