vetty — Pet-owner Mobile App
A Flutter (Dart) consumer app for pet owners. Lives at vetyy/ in the repo. Targets iOS, Android, web (Flutter web).
See the audit for the security and UX punch-list. This page is the feature catalogue.
Top-level navigation
The home shell carries five tabs:
- Home — pets carousel + quick actions (Add pet, Find a clinic, Lost pet toggle).
- Pets — list of owned pets + "Shared with me" caregiver section.
- Health — health journal entries across all pets, vaccinations-due feed.
- Orders & invoices — outstanding invoices + order request status.
- Profile — account settings, sessions, sign out.
Pet detail screen
The richest screen in the app. Tabs:
| Tab | Backed by |
|---|---|
| Profile | pet row + public-profile toggles |
| Vaccinations | pet_vaccinations (CRUD), "due soon" inline |
| Documents | pet_documents (upload, signed-URL download) |
| Health journal | pet_health_logs (owner-write entries) |
| Caregivers | pet_caregivers (invite / resend / revoke) |
| Timeline | unified /timeline endpoint |
| Public profile | collar tag + lost-pet toggle |
Auth
vetyy/lib/core/auth/:
auth_store.dart— token storage now usesflutter_secure_storage(keychain on iOS, encrypted SharedPreferences on Android). This was P0-11a in the Week 3 remediation.api_client.dart— single HTTP wrapper. Bearer header attached per request. Talks toEnv.apiBasewhich comes from--dart-define=VETTY_API_BASE=…at build time.
Release-build env-var guard
config/env.dart exposes Env.assertReleaseOverride() which
main() calls before runApp. In release builds it throws a
StateError unless VETTY_API_BASE_OVERRIDDEN=true was set at build
time — a mis-configured CI ship gets caught on first launch rather
than silently pointing customer devices at staging.
Features by domain
Marketplace
- Find a clinic / pharmacy / groomer / breeder / trainer — hits
GET /providerswithbusiness_type+ geo filter (with location permission). - Detail screen → tap into provider profile.
- "Place order" (pharmacy / retail) routes to the order-request flow.
- "Book appointment" — top item on the pet-detail action menu.
Owner picks reason + preferred date/time + optional note; POSTs to
/owner/me/appointmentswithbooking_source='owner_app'. The clinic sees the row asscheduledawaiting confirmation and confirms / reschedules through their standard queue.
Pet sharing & caregivers
- Share pet → QR / code — generates a pet share token via
POST /me/shares. The QR is the entry point for vetdoctor's scan flow. - Caregivers — primary owner can invite an email + access level. Resend / revoke from the same screen. The "Shared with me" tab on the pets list shows pets you've been invited to.
Lost pet & passport
- Public profile toggle on each pet — mints a slug, shows the QR pointing to
/p/{slug}. - Lost mode — bigger toggle on the public profile screen. Sets the lost message, exposes contact info on the public landing page.
- Pet passport PDF —
GET /me/pets/{pet}/passport.pdf— downloadable. Includes the QR and vaccination history.
Vaccinations
- Per-pet list + create / edit / delete.
- Cross-pet "Due Soon" feed on the Health tab — pulled from
/me/vaccinations/due(defaults to a 30-day horizon). - Vaccination certificate PDF (whole-pet or single-vaccine via
?vaccination_id=) for travel paperwork.
Health journal
- Owner-written entries (weight, temperature, appetite, symptoms, notes).
- Visible to active full-access caregivers.
- Same controller surfaces clinic-recorded vitals (e.g. check-in weight) — those entries display with a clinic icon and the clinic name.
Documents
- Upload categories:
lab_report,xray,ultrasound,insurance,id,other. - Stored on the private disk; downloads use a short-lived signed URL.
Orders (commerce)
- Refill — built from a past prescription via
GET /me/pets/{pet}/prescriptions/{prescription}/refill-draft. - Reorder — repeat a non-Rx purchase from a tenant the owner has used before.
- Custom — open ping to a marketplace pharmacy / retail tenant.
- Status updates land on the Orders tab; the seller email contains a tracking link for the simple case.
Billing & payments
- Invoice list + detail + receipt PDF.
- Razorpay payment SDK launched from the invoice detail;
verifyruns after the SDK callback. - Cross-tenant statement PDF (
/me/statement.pdf) for accountant exports.
Auth + password reset
- Email + password login.
- Self-serve registration (account_type='owner').
- Forgot-password → reset flow over the same endpoints documented in Authentication.
Daily-use loops (per-pet)
Four screens designed for high-frequency owner tasks:
- Medications — schedules (dose + time-slots) + per-dose log with mark-given/skip. Local notifications fire at each slot until the next dose is logged.
- Feeding tracker — food catalog search + calorie summary with a target computed from the Resting Energy Requirement formula.
- Activity — walk / play / swim log + 90-day heatmap.
- Weight chart — full-screen
fl_chartline chart plus a sparkline on the pet-detail summary card.
Quick wins
- Pet share card — 1080×1080 PNG for Instagram / WhatsApp status.
Rendered via
RepaintBoundary.toImage, dispatched throughshare_plus. - Ask my vet — WhatsApp / SMS / call / copy sheet pre-filled with the clinic's contact info.
- Confetti — birthday-today check on the pet, vaccination-add celebration.
- Combined spend summary — 12-month bar strip on the invoices screen.
- Quick-add FAB on home — walk / meal / weight without leaving home.
- Per-pet notification mute — SharedPreferences-backed set that the notification scheduler and the pet-detail chip both read.
Health-journal photo
An owner-uploaded photo can attach to any journal entry. The FK
column photo_document_id links to pet_documents; the journal card
renders an inline 96×96 thumbnail (bearer-authed) with tap-to-open in
the system viewer via ?token= query param (TokenFromQuery
middleware promotes it to an Authorization header).
In-app notifications
The bell icon in the app bar polls
/me/notifications/unread-count every 60 s and shows the badge.
Tapping opens the inbox screen with swipe-to-delete + tap-to-mark-
read. Deeplinks (vetty://pet/<id>, vetty://invoice/<id>, etc.)
route into the matching screens. See
Notifications for the full
channel design.
Internationalization
Foundation lives at lib/core/i18n/i18n.dart. English and Hindi seed
strings across common, auth, pets, and booking namespaces. main()
calls await Vi18n.init() before render so the persisted locale
hydrates from SharedPreferences. Full sweep across every screen is
still a follow-up sprint.
State management
Provider + setState per screen. The audit's FutureBuilder race
issue is fixed; list-key reuse was addressed in the vetyy #5–#8
sweep.
Known gaps (selected)
- No offline mode / outbox queue.
- No FCM / APNs push (in-app inbox is HTTP-pull only — see the notifications module doc for the roadmap).
- Full i18n coverage on every screen (~30 strings seeded today).
- Dark mode.
See AUDIT.md §8 for the full list.